Deprecated: Assigning the return value of new by reference is deprecated in /home/byteflex/public_html/wiki/inc/parserutils.php on line 208

Deprecated: Assigning the return value of new by reference is deprecated in /home/byteflex/public_html/wiki/inc/parserutils.php on line 211

Deprecated: Assigning the return value of new by reference is deprecated in /home/byteflex/public_html/wiki/inc/parserutils.php on line 421

Deprecated: Assigning the return value of new by reference is deprecated in /home/byteflex/public_html/wiki/inc/parserutils.php on line 594

Deprecated: Function split() is deprecated in /home/byteflex/public_html/wiki/inc/auth.php on line 154

Warning: Cannot modify header information - headers already sent by (output started at /home/byteflex/public_html/wiki/inc/parserutils.php:208) in /home/byteflex/public_html/wiki/inc/auth.php on line 245

Warning: Cannot modify header information - headers already sent by (output started at /home/byteflex/public_html/wiki/inc/parserutils.php:208) in /home/byteflex/public_html/wiki/inc/actions.php on line 370

Warning: Cannot modify header information - headers already sent by (output started at /home/byteflex/public_html/wiki/inc/parserutils.php:208) in /home/byteflex/public_html/wiki/inc/actions.php on line 374
====== Eventum ====== What is Eventum? [[http://www.mysql.com/products/eventum|Eventum]] is a web based issue management tool based on PHP and MySql. ====== Integration ====== ===== Why Integrate Dokuwiki and Eventum? ===== Integration between Dokuwiki and Eventum permits use of Dokuwiki as a store for notes on issues and in turn, linking those notes to other Wiki pages. When searching in Dokuwiki, any troublesome items can have links back to their issue pages in Eventum, giving another dimension to the Wiki and leveraging the power of the two combined applications. ===== What is Integrated? ===== * **Single sign on**: login or logout on either Dokuwiki or Eventum and the users login status will be the same on both systems. * **[[wp>Interwiki]] links between Dokuwiki and Eventum**: Allows users to create links in Dokuwiki that reference issues in Eventum. * **Project and Issue links in Eventum**: Allow users to jump straight to pages in Dokuwiki and create them if they do not yet exist. * **Eventum ACL propagated into Dokuwiki**: Permissions for Eventum users, groups, projects and access levels can be used in the Dokuwiki ACL to achieve fine grained control of access to any page in Dokuwiki. ====== Code changes ====== ===== Changes to Eventum ===== ==== Database ==== In order to support querying Eventum users roles from within Dokuwiki, an additional table must be created in the MySql database used by Eventum.\\ The SQL below has been exported from MySql using phpadmin. This code should be used to create the role lookup table. -- phpMyAdmin SQL Dump -- version 2.11.2.2 -- http://www.phpmyadmin.net -- -- Host: localhost:3306 -- Generation Time: Sep 02, 2008 at 02:20 PM -- Server version: 5.0.51 -- PHP Version: 5.2.6 SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; -- -- Database: `eventum` -- -- -------------------------------------------------------- -- -- Table structure for table `eventum_role` -- CREATE TABLE IF NOT EXISTS `eventum_role` ( `role_id` tinyint(4) NOT NULL default '0', `role_name` varchar(20) NOT NULL, PRIMARY KEY (`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- -- Dumping data for table `eventum_role` -- INSERT INTO `eventum_role` (`role_id`, `role_name`) VALUES (1, 'Viewer'), (2, 'Reporter'), (3, 'Customer'), (4, 'Standard User'), (5, 'Developer'), (6, 'Manager'), (7, 'Administrator'); ==== Files ==== All file paths are relative to the eventum directory. === ./config/config.php === So that the Dokuwiki cookies can be changed by Eventum, change the ''APP_COOKIE_URL'' and define ''DOKU_REL'': //define('APP_COOKIE_URL', APP_RELATIVE_URL); define('APP_COOKIE_URL', '/'); define('DOKU_REL', '/'); === ./logout.php === To enable Eventum to log users out of Dokuwiki when logging out of Eventum, code should be changed to: require_once(dirname(__FILE__) . "/init.php"); require_once(APP_INC_PATH . "class.auth.php"); // define DokuWiki cookie and session id if (!defined('DOKU_COOKIE')) define('DOKU_COOKIE', 'DW'.md5(DOKU_REL)); Auth::removeCookie(APP_COOKIE); setcookie(DOKU_COOKIE, '', time()-600000, DOKU_REL); if(isset($_SESSION[DOKU_COOKIE]['auth']['user'])) unset($_SESSION[DOKU_COOKIE]['auth']['user']); if(isset($_SESSION[DOKU_COOKIE]['auth']['pass'])) unset($_SESSION[DOKU_COOKIE]['auth']['pass']); if(isset($_SESSION[DOKU_COOKIE]['auth']['info'])) unset($_SESSION[DOKU_COOKIE]['auth']['info']); if(isset($_SERVER['REMOTE_USER'])) unset($_SERVER['REMOTE_USER']); $USERINFO=null; //FIXME // Remove session cookie too. setcookie('DokuWiki', '', time()-600000, DOKU_REL); // if 'remember projects' is true don't remove project cookie $project_cookie = Auth::getCookieInfo(APP_PROJECT_COOKIE); if (empty($project_cookie['remember'])) { Auth::removeCookie(APP_PROJECT_COOKIE); } Auth::redirect(APP_RELATIVE_URL . "index.php?err=6"); === ./include/class.auth.php === Change the ''createLoginCookie'' function to: function createLoginCookie($cookie_name, $email, $expire=0) { $time = time(); $cookie = array( "email" => $email, "login_time" => $time, "hash" => md5($GLOBALS["private_key"] . md5($time) . $email), ); $cookie = base64_encode(serialize($cookie)); if ($expire == 0) { $expire = APP_COOKIE_EXPIRE; } Auth::setCookie($cookie_name, $cookie, $expire); } This allows the eventum cookie to be set with a longer expiration date which can be passed from the Dokuwiki ''eventum.class.php'' ''trustExternal'' method. === ./templates/navigation.tpl.html === To add a link to the main project page within Dokuwiki, change the navigation code: diff -c /apps/src/eventum-2.1.1/templates/navigation.tpl.html .//navigation.tpl.html *** /apps/src/eventum-2.1.1/templates/navigation.tpl.html Sat Nov 10 00:52:36 2007 --- .//navigation.tpl.html Tue Jul 22 15:21:27 2008 *************** *** 8,13 **** --- 8,16 ---- {$app_setup.tool_caption|default:$application_title} ({t}Logout{/t}) + {if $current_role > $roles.reporter} + {t}Project @ Wiki{/t} | + {/if} {if $current_role > $roles.developer} {t}Administration{/t} | {/if} Only in ./: view_form.tpl.html.bak === ./templates/view_form.tpl.html === To create links to Dokuwiki from the issue view page, alter the "Issue Overview" code to: diff -c /apps/src/eventum-2.1.1/templates/view_form.tpl.html .//view_form.tpl.html *** /apps/src/eventum-2.1.1/templates/view_form.tpl.html Sat Nov 10 00:52:16 2007 --- .//view_form.tpl.html Fri Jul 18 16:53:17 2008 *************** *** 178,184 **** ===== Changes to Dokuwiki ===== ==== ./conf/local.php ==== Add the following setup lines to your local.php file: /* Eventum Single Sign on */ $conf['useacl'] = 1; // Use Access Control Lists to restrict access? $conf['authtype'] = 'eventum'; //Auth type define( EVENTUM_ROOT, '/path/to/apache2/htdocs/eventum/' ); // file path to Eventum, must terminate with / define( EVENTUM_URL, 'http://myhost/eventum/' ); // url to Eventum, must terminate with / define( EVENTUM_REL, '/eventum/' ); // relative url to Eventum, must terminate with / ==== ./conf/interwiki.conf ==== Add an appropriate entry to the interwiki list: issue http://myhost/eventum/view.php?id= ==== ./inc/auth.php ==== At about line 25, add: global $USERINFO; At about line 78, I changed the code to allow the Dokuwiki ACL placeholder ''%%@USER@%%'' to represent the user id instead of the user name. This seems to work better with my Eventum authentication which ordinarily uses an email address for logging in rather than a user id. Also the ''$_SERVER'' array sometimes appeared unset - undoubtedly triggered by the Eventum authentication and cookie handling: if(is_readable(DOKU_CONF.'acl.auth.php')){ $AUTH_ACL = file(DOKU_CONF.'acl.auth.php'); if(isset($_SERVER['REMOTE_USER'])){ $AUTH_ACL = str_replace('@USER@',$_SERVER['REMOTE_USER'],$AUTH_ACL); } elseif (isset($USERINFO['user'])) { $AUTH_ACL = str_replace('@USER@',$USERINFO['user'],$AUTH_ACL); } }else{ $AUTH_ACL = array(); } Also, at around line 468 (in the ''auth_nameencode'' function) I changed the regular expressions to permit underscore characters in the ACL. The underscore chars are used in the ''eventum.class.php'' when converting spaces in user names, which is much easier than trying to encode all the Eventum user names with ''%%%5f%%''! I believe this should be standard in Dokuwiki anyway as regexp's usually permit underscore chars without having to escape them. if (!isset($cache[$name][$skip_group])) { if($skip_group && $name{0} =='@'){ $cache[$name][$skip_group] = '@'.preg_replace('/([\x00-\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\x7f])/e', "'%'.dechex(ord(substr('\\1',-1)))",substr($name,1)); }else{ $cache[$name][$skip_group] = preg_replace('/([\x00-\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\x7f])/e', "'%'.dechex(ord(substr('\\1',-1)))",$name); } } ==== ./inc/auth/eventum.class.php ==== Create this file, it contains most of the code to make Eventum authentication work with Dokuwiki. Notice in ''getGroups'' the role table is used as we defined earlier in the Eventum DB. */ if(!defined('EVENTUM_ROOT')) define('EVENTUM_ROOT', '/path/to/apache2/htdocs/eventum/'); require_once(EVENTUM_ROOT.'init.php'); require_once(DOKU_INC.'inc/auth/mysql.class.php'); class auth_eventum extends auth_mysql { /** * Constructor. * * Sets additional capabilities and config strings */ function auth_eventum(){ global $conf; $this->cando['logoff'] = true; $this->cando['external'] = true; // make sure we use a crypt understood by eventum $conf['passcrypt'] = 'md5'; // get global vars from PunBB config $db_host = APP_SQL_DBHOST; $db_name = APP_SQL_DBNAME; $db_username = APP_SQL_DBUSER; $db_password = APP_SQL_DBPASS; $db_prefix = APP_TABLE_PREFIX; // now set up the mysql config strings $conf['auth']['mysql']['server'] = $db_host; $conf['auth']['mysql']['user'] = $db_username; $conf['auth']['mysql']['password'] = $db_password; $conf['auth']['mysql']['database'] = $db_name; $conf['auth']['mysql']['checkPass'] = "SELECT u.usr_password AS pass FROM ${db_prefix}user AS u WHERE u.usr_status = 'active' AND LOWER(REPLACE(u.usr_full_name,' ','_')) = LOWER(REPLACE('%{user}',' ','_'))"; $conf['auth']['mysql']['getUserInfo'] = "SELECT LOWER(REPLACE(usr_full_name,' ','_')) AS user, usr_password AS pass, usr_full_name AS name, usr_email AS mail, usr_id as id, grp_name as `group` FROM ${db_prefix}user AS u LEFT JOIN ${db_prefix}group AS g ON g.grp_id = u.usr_grp_id WHERE LOWER(REPLACE(u.usr_full_name,' ','_')) = LOWER(REPLACE('%{user}',' ','_'))"; $conf['auth']['mysql']['getUserInfoByEmail'] = "SELECT LOWER(REPLACE(usr_full_name,' ','_')) AS user, usr_password AS pass, usr_full_name AS name, usr_email AS mail, usr_id as id, grp_name as `group` FROM ${db_prefix}user AS u LEFT JOIN ${db_prefix}group AS g ON g.grp_id = u.usr_grp_id WHERE u.usr_email = '%{mail}'"; $conf['auth']['mysql']['getGroups'] = "SELECT g.grp_name as `group` FROM ${db_prefix}user AS u, ${db_prefix}group AS g WHERE u.usr_grp_id = g.grp_id AND LOWER(REPLACE(u.usr_full_name,' ','_')) = LOWER(REPLACE('%{user}',' ','_')) UNION SELECT DISTINCT CONCAT(UPPER(REPLACE(r.role_name,' ','_')),'!',REPLACE(p.prj_title,' ','_')) as `group` FROM ${db_prefix}user AS u2 LEFT JOIN ${db_prefix}project_user AS pu ON pu.pru_usr_id = u2.usr_id LEFT JOIN ${db_prefix}role AS r ON r.role_id = pu.pru_role LEFT JOIN ${db_prefix}project AS p ON p.prj_id = pu.pru_prj_id WHERE LOWER(REPLACE(u2.usr_full_name,' ','_')) = LOWER(REPLACE('%{user}',' ','_'))"; $conf['auth']['mysql']['getUsers'] = "SELECT DISTINCT LOWER(REPLACE(u.usr_full_name,' ','_')) AS user FROM ${db_prefix}user AS u LEFT JOIN ${db_prefix}group AS g ON g.grp_id = u.usr_grp_id WHERE u.usr_id IS NOT NULL"; $conf['auth']['mysql']['FilterLogin'] = "LOWER(REPLACE(u.usr_full_name,' ','_')) LIKE LOWER(REPLACE('%{user}',' ','_'))"; $conf['auth']['mysql']['FilterName'] = "u.usr_full_name LIKE '%{name}'"; $conf['auth']['mysql']['FilterEmail'] = "u.usr_email LIKE '%{email}'"; $conf['auth']['mysql']['FilterGroup'] = "g.grp_name LIKE '%{group}'"; $conf['auth']['mysql']['SortOrder'] = "ORDER BY u.usr_full_name"; $conf['auth']['mysql']['addUser'] = "INSERT INTO ${db_prefix}user (usr_full_name, usr_password, usr_email, usr_status, usr_created_date) VALUES ('%{name}', '%{pass}', '%{email}', 'active', NOW())"; $conf['auth']['mysql']['addGroup'] = "INSERT INTO ${db_prefix}group (grp_name, grp_description) VALUES ('%{group}', CONCAT('%{group}',' group')"; $conf['auth']['mysql']['addUserGroup']= "UPDATE ${db_prefix}user SET usr_grp_id=%{gid} WHERE usr_id='%{uid}'"; // $conf['auth']['mysql']['delGroup'] = "DELETE FROM ${db_prefix}group WHERE grp_id='%{gid}'"; $conf['auth']['mysql']['getUserID'] = "SELECT usr_id FROM ${db_prefix}user WHERE LOWER(REPLACE(usr_full_name,' ','_'))=LOWER(REPLACE('%{user}',' ','_'))"; $conf['auth']['mysql']['updateUser'] = "UPDATE ${db_prefix}user SET"; $conf['auth']['mysql']['UpdateLogin'] = "usr_full_name='%{name}'"; $conf['auth']['mysql']['UpdatePass'] = "usr_password='%{pass}'"; $conf['auth']['mysql']['UpdateEmail'] = "usr_email='%{email}'"; // $conf['auth']['mysql']['UpdateName'] = "realname='%{name}'"; $conf['auth']['mysql']['UpdateTarget']= "WHERE usr_id=%{uid}"; $conf['auth']['mysql']['delUserGroup']= "UPDATE ${db_prefix}user SET usr_grp_id=NULL WHERE usr_id=%{uid}"; $conf['auth']['mysql']['getGroupID'] = "SELECT grp_id AS id FROM ${db_prefix}group WHERE grp_name='%{group}'"; $conf['auth']['mysql']['TablesToLock']= array("${db_prefix}user", "${db_prefix}user AS u", "${db_prefix}user AS u2", "${db_prefix}group", "${db_prefix}group AS g", "${db_prefix}project", "${db_prefix}project AS p", "${db_prefix}project_user", "${db_prefix}project_user AS pu", "${db_prefix}role", "${db_prefix}role AS r"); $conf['auth']['mysql']['debug'] = 1; // call mysql constructor $this->auth_mysql(); } /** * Just checks against the $pun_user variable */ function trustExternal($user,$pass,$sticky=false){ global $USERINFO; global $conf; $sticky ? $sticky = true : $sticky = false; //sanity check // Set expiration time to 60 days if "Remember Me" is checked. $expire = ($sticky) ? time() + 60*60*24*60 : 0; // someone used the login form if(!empty($user)){ if($this->checkPass($user,$pass)){ # Must get email so we can set the eventum cookie. # Use standard sql to get the email. $eve_data = $this->getUserData($user); $email = $eve_data['mail']; if(empty($email)) { return false; } Session::init($eve_data['id']); // Set eventum cookie Auth::createLoginCookie(APP_COOKIE, $email, $expire); Auth::saveLoginAttempt($user, 'success'); // set dokuwiki cookie $eve_data['pass'] = PMA_blowfish_encrypt($eve_data['pass'],auth_cookiesalt()); $doku_cookie = base64_encode(cleanID($eve_data['user'])."|$sticky|".$eve_data['pass']); setcookie(DOKU_COOKIE,$doku_cookie,$expire,DOKU_REL); } } else { $cookie = $_COOKIE[APP_COOKIE]; $eve_cookie = unserialize(base64_decode($cookie)); if (isset($_COOKIE[APP_COOKIE])) { if (Auth::isValidCookie($eve_cookie)) { $email = $eve_cookie['email']; $eve_data = $this->getUserDataByEmail($email); $eve_data['pass'] = PMA_blowfish_encrypt($eve_data['pass'],auth_cookiesalt()); } } } if (!isset($eve_data)) { // Unset cookie Auth::removeCookie(APP_COOKIE); //invalid credentials - log off msg($lang['badlogin'],-1); auth_logoff(); return false; } // check the session Session::verify($eve_data['id']); // okay we're logged in - set the globals $USERINFO['pass'] = $eve_data['pass']; $USERINFO['name'] = $eve_data['name']; $USERINFO['mail'] = $eve_data['mail']; $USERINFO['grps'] = $eve_data['grps']; $_SERVER['REMOTE_USER'] = $eve_data['user']; $_SESSION[DOKU_COOKIE]['auth']['user'] = $eve_data['user']; $_SESSION[DOKU_COOKIE]['auth']['pass'] = $eve_data['pass']; $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid(); $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO; $_SESSION[DOKU_COOKIE]['auth']['time'] = time(); return true; } /** * remove eventum cookie on logout */ function logOff(){ Auth::removeCookie(APP_COOKIE); } /** * [public function] * * Returns the usr_id based on supplied usr_email * * @param $user user's nick to get data for * @return user info * * @author Chris Usher */ function getUserDataByEmail($email){ if($this->_openDB()) { $this->_lockTables("READ"); $info = $this->_getUserDataByEmail($email); $this->_unlockTables(); $this->_closeDB(); } else $info = false; return $info; } /** * Retrieves the user id of a given user email * * The database connection must already be established * for this function to work. Otherwise it will return * 'false'. * * @param $email user whose data is desired * @return bool false on error * @return array user info on success * * @author Chris Usher */ function _getUserDataByEmail($email) { if($this->dbcon) { $sql = str_replace('%{mail}',$this->_escape($email),$this->cnf['getUserInfoByEmail']); $result = $this->_queryDB($sql); if($result !== false && count($result)) { $info = $result[0]; $info['grps'] = $this->_getGroups($info['name']); return $info; } } return false; } } //Setup VIM: ex: et ts=2 enc=utf-8 : ===== Admin settings in Eventum ===== ==== Groups ==== We have groups defined in mixed case, e.g: ''ITDevTeam'', ''ITSuppTeam'' and assigned to one or more projects. ==== Users ==== We have users identified by their email address and with a "Full Name" like 'Chris Usher'. The space in the "Full Name" is converted to an underscore (_) by ''eventum.class.php''. ==== Projects ==== Projects may be identified simply with one or more words, and have been proven to work with mixed case, spaces and underscore characters as part of the project name. ===== Dokuwiki ACL ===== With projects, users and groups correctly identified in Eventum, the following lines should be valid in the dokuwiki ACL (''./conf/acl.auth.php''): # acl.auth.php # # Note: use the following characters to encode the strings: # Sp = %20 # ! = %21 # All characters other than [0-9a-zA-Z_] should be encoded! # # Example: #* @DEVELOPER%21Adhoc_Tasks 1 # * @ALL 0 * @ITDevTeam 1 * @ITSuppTeam 1 * @ITProjMan 1 * @ITOps 1 * @ITHelpDesk 1 start @ALL 1 adhoc_tasks:* @VIEWER%21Adhoc_Tasks 1 adhoc_tasks:* @REPORTER%21Adhoc_Tasks 2 adhoc_tasks:* @CUSTOMER%21Adhoc_Tasks 1 adhoc_tasks:* @STANDARD_USER%21Adhoc_Tasks 8 adhoc_tasks:* @DEVELOPER%21Adhoc_Tasks 8 adhoc_tasks:* @MANAGER%21Adhoc_Tasks 8 adhoc_tasks:* @ADMINISTRATOR%21Adhoc_Tasks 8 itdev:* @ITDevTeam 8 itdev:* @ITSuppTeam 4 itdev:* @ITOps 4 itdev:* @ITProjMan 8 minutes:* @ALL 1 minutes:itdev:* @ITDevTeam 8 minutes:itdev:* @ITSuppTeam 8 playground:* @ALL 8 blog:* @ALL 1 blog:* @USER@ 4 blog:@USER@:* @USER@ 8 blog:private:@USER@:* @ALL 0 blog:private:@USER@:* @USER@ 16 user:* @ALL 1 user:@USER@:* @USER@ 8 user:private:@USER@:* @ALL 0 user:private:@USER@:* @USER@ 16 In the above example you can see how a mixture of fine grain ACL control techniques can be used. By specifying the ''%%@ITDevTeam%%'' group in the ACL, it represents any Eventum user who is a member of the "ITDevTeam" group. By specifying the ''%%@VIEWER%21Adhoc_Tasks%%'' group in the ACL, it represents any Eventum user who has the role "Viewer" on project "Adhoc Tasks".\\ The ''%%%21%%'' or encoded ''!'' in the above group name is simply a divider between the role and project name that is inserted by the SQL queries defined in ''eventum.class.php''. Note also that role names are uppercased when specified in the Dokuwiki ACL, this is to make them easier to read: roles are in upper case, project names are in whatever case they were defined in Eventum. ==== ACL editing ==== Try not to edit the ACL using the web interface (Admin option) if using ''%%@USER@%%'' in the ACL, as all occurrences are converted to the name of the user making changes, therefore breaking the ACL and undoing the flexibility of having the macro there in the first place. ===== Namespaces in Dokuwiki ===== For each issue in Eventum, when you click on a link that redirects to Dokuwiki, the Dokuwiki page name will be like ''project_name:issues:42'', where the project under Eventum was something like "Project Name", and the issue was no. "42". So it is best to set up the namespace structure in Dokuwiki when creating a new Eventum project (this is something I haven't automated yet as we don't have new projects appearing every day). So pages that it is beneficial to have (using the example above) are: project_name:home project_name:issues:_template.txt The ''home'' page may be any page name, but will have to be set in the Eventum ''navigation.tpl.html'' file too. The link is hard coded :-( By using a namespace template (''_template.txt'') when setting up a new project, it gives you the option to keep all issue pages looking the same.\\ Here is a copy of ours: ====== Issue @PAGE@ ====== ^Issue Link:|[[issue>@PAGE@]]| ^First Added:| //[[@MAIL@|@NAME@]] @DATE@//| ===== Title... ===== ===== Credits ===== Inspiration for integrating Dokuwiki and Eventum came from the Dokuwiki/Mantis integration at [[http://www.mantisbt.org/wiki/doku.php/mantisbt:issue:7075:integration_with_dokuwiki|Mantisbt]]. Many thanks to Andi and the folks behind [[http://www.dokuwiki.org|Dokuwiki]] for creating such a neat and flexible Wiki core. ~~DISCUSSION:off~~
! {t}Issue Overview{/t} (ID: {$issue.iss_id}) {if $current_role > $roles.customer} --- 178,184 ---- ! --- 402,408 ---- ! *************** *** 443,448 **** --- 443,454 ---- {/literal} +
! {t}Issue Overview{/t} (ID: {$issue.iss_id}) (Issue @ Wiki) {if $current_role > $roles.customer} *************** *** 402,408 **** {t}Group{/t}: {$issue.group.grp_name}
{t}Group{/t}: {$issue.group.grp_name}
+
+  [ Issue @ Wiki ] +
+ +