/** * Map Resource Data * * @return void */ private function _mapResourceData() { // do we want to do a title match? if ($this->_options['titlematch'] == 1 && isset($this->record->type->id)) { $sql = 'SELECT id, title, LEVENSHTEIN( title, ' . $this->_database->quote($this->raw->title) . ' ) as titleDiff FROM `#__resources` WHERE `type`=' . $this->record->type->id . ' HAVING titleDiff < ' . self::TITLE_MATCH; $this->_database->setQuery($sql); $results = $this->_database->loadObjectList('id'); // did we get more then one result? if (count($results) > 1) { $ids = implode(", ", array_keys($results)); throw new Exception(Lang::txt('COM_RESOURCES_IMPORT_RECORD_MODEL_UNABLE_DETECTDUPLICATE', $ids)); } // if we only have one were all good if (count($results) == 1) { // set our id to the matched resource $resource = reset($results); $this->raw->id = $resource->id; // add a notice with link to resource matched $resourceLink = rtrim(str_replace('administrator', '', \Request::base()), DS) . DS . 'resources' . DS . $resource->id; $link = '<a target="_blank" href="' . $resourceLink . '">' . $resourceLink . '</a>'; array_push($this->record->notices, Lang::txt('COM_RESOURCES_IMPORT_RECORD_MODEL_MATCHEDBYTITLE', $link)); } } // do we have a resource id // either passed in the raw data or gotten from the title match if (isset($this->raw->id) && $this->raw->id > 1) { $this->record->resource->load($this->raw->id); } else { $this->raw->standalone = 1; $this->raw->created = \Date::toSql(); $this->raw->created_by = $this->_user->get('id'); // publish up/down if (!isset($this->raw->publish_up)) { $this->raw->publish_up = \Date::toSql(); } if (!isset($this->raw->publish_down)) { $this->raw->publish_down = '0000-00-00 00:00:00'; } } // set modified date/user $this->raw->modified = \Date::toSql(); $this->raw->modified_by = $this->_user->get('id'); // set status if (isset($this->_options['status'])) { $this->raw->published = (int) $this->_options['status']; } // set group if (isset($this->_options['group'])) { $this->raw->group_owner = $this->_options['group']; } // set access if (isset($this->_options['access'])) { $this->raw->access = (int) $this->_options['access']; } // bind resource data $this->record->resource->bind($this->raw); // resource params $params = new \Hubzero\Config\Registry($this->record->resource->params); $this->record->resource->params = $params->toString(); // resource attributes $attribs = new \Hubzero\Config\Registry($this->record->resource->attribs); $this->record->resource->attribs = $attribs->toString(); // full text pieces - to add paragraph tags $fullTextPieces = array_map("trim", explode("\n", $this->record->resource->introtext)); $fullTextPieces = array_values(array_filter($fullTextPieces)); // set the full text $this->record->resource->fulltxt = "<p>" . implode("</p>\n<p>", $fullTextPieces) . "</p>"; if (!isset($this->raw->custom_fields)) { $this->raw->custom_fields = array(); } // bind custom fields to types custom fields if (isset($this->record->type->id)) { $resourcesElements = new \Components\Resources\Models\Elements((array) $this->raw->custom_fields, $this->record->type->customFields); $customFieldsHtml = $resourcesElements->toDatabaseHtml(); // add all custom fields to custom object foreach ($resourcesElements->getSchema()->fields as $field) { $fieldLabel = $field->label; $fieldName = $field->name; $value = isset($this->raw->custom_fields->{$fieldName}) ? $this->raw->custom_fields->{$fieldName} : null; if ($field->type == 'hidden') { $value = isset($field->options[0]) ? $field->options[0]->value : $value; } $this->record->custom->{$fieldLabel} = $value; } } else { $customFieldsHtml = ''; } // add custom fields to fulltxt $this->record->resource->fulltxt .= "\n\n" . $customFieldsHtml; }
/** * Display forms for editing/creating a reosurce * * @return void */ public function displayTask() { // Incoming $alias = Request::getVar('app', ''); $version = Request::getVar('editversion', 'dev'); $step = Request::getInt('step', 1); // Load the tool $obj = new \Components\Tools\Tables\Tool($this->database); $this->_toolid = $obj->getToolId($alias); if (!$this->_toolid) { App::redirect(Route::url('index.php?option=' . $this->_option . '&controller=pipeline')); return; } // make sure user is authorized to go further if (!$this->_checkAccess($this->_toolid)) { App::abort(403, Lang::txt('COM_TOOLS_ALERTNOTAUTH')); return; } $nextstep = $step + 1; // get tool version (dev or current) information $obj->getToolStatus($this->_toolid, $this->_option, $status, $version); // get resource information $row = new \Components\Resources\Tables\Resource($this->database); $row->loadAlias($alias); $row->alias = $row->alias ? $row->alias : $alias; if (!$status['fulltxt']) { $status['fulltxt'] = $row->fulltxt; } // process first step if ($nextstep == 3 && (isset($_POST['nbtag']) || isset($_POST['fulltxt']))) { if (!isset($_POST['fulltxt']) || !trim($_POST['fulltxt'])) { $this->setError(Lang::txt('COM_TOOLS_REQUIRED_FIELD_CHECK', 'Abstract')); $step = 1; $nextstep--; } $hztv = \Components\Tools\Helpers\Version::getToolRevision($this->_toolid, $version); $objV = new \Components\Tools\Tables\Version($this->database); if (!$objV->bind($_POST)) { $this->setError($objV->getError()); return; } $body = $this->txtClean($_POST['fulltxt']); if (preg_match("/([\\<])([^\\>]{1,})*([\\>])/i", $body)) { // Do nothing $status['fulltxt'] = trim(stripslashes($body)); } else { // Wiki format will be used $status['fulltxt'] = Request::getVar('fulltxt', $status['fulltxt'], 'post'); } // Get custom areas, add wrapper tags, and compile into fulltxt $type = new \Components\Resources\Tables\Type($this->database); $type->load($row->type); include_once PATH_CORE . DS . 'components' . DS . 'com_resources' . DS . 'models' . DS . 'elements.php'; $elements = new \Components\Resources\Models\Elements(array(), $type->customFields); $schema = $elements->getSchema(); $fields = array(); foreach ($schema->fields as $field) { $fields[$field->name] = $field; } $nbtag = Request::getVar('nbtag', array()); $found = array(); foreach ($nbtag as $tagname => $tagcontent) { $f = ''; $status['fulltxt'] .= "\n" . '<nb:' . $tagname . '>'; if (is_array($tagcontent)) { $c = count($tagcontent); $num = 0; foreach ($tagcontent as $key => $val) { if (trim($val)) { $num++; } $status['fulltxt'] .= '<' . $key . '>' . trim($val) . '</' . $key . '>'; } if ($c == $num) { $f = 'found'; } } else { $f = trim($tagcontent); if ($f) { $status['fulltxt'] .= trim($tagcontent); } } $status['fulltxt'] .= '</nb:' . $tagname . '>' . "\n"; if (!$f && isset($fields[$tagname]) && $fields[$tagname]->required) { $this->setError(Lang::txt('COM_TOOLS_REQUIRED_FIELD_CHECK', $fields[$tagname]->label)); } $found[] = $tagname; } foreach ($fields as $field) { if (!in_array($field->name, $found) && $field->required) { $found[] = $field->name; $this->setError(Lang::txt('COM_TOOLS_REQUIRED_FIELD_CHECK', $field->label)); } } $hztv->fulltxt = $objV->fulltxt = $status['fulltxt']; $hztv->description = $objV->description = \Hubzero\Utility\String::truncate(Request::getVar('description', $status['description'], 'post'), 500); $hztv->title = $objV->title = \Hubzero\Utility\String::truncate(preg_replace('/\\s+/', ' ', Request::getVar('title', $status['title'], 'post')), 500); if (!$hztv->update()) { throw new Exception(Lang::txt('COM_TOOLS_ERROR_UPDATING_TOOL'), 500); } else { // get updated tool status $obj->getToolStatus($this->_toolid, $this->_option, $status, $version); } if ($version == 'dev') { // update resource page $this->updatePage($row->id, $status); } } // Group access //$accesses = array('Public', 'Registered', 'Special', 'Protected', 'Private'); //$lists = array(); //$lists['access'] = \Components\Tools\Helpers\Html::selectAccess($accesses, $row->access); //$groups = \Hubzero\User\Helper::getGroups(User::get('id'), 'members'); // get authors $objA = new \Components\Tools\Tables\Author($this->database); $authors = $version == 'current' ? $objA->getToolAuthors($version, $row->id, $status['toolname']) : array(); // Tags $tags = Request::getVar('tags', '', 'post'); $tagfa = Request::getVar('tagfa', '', 'post'); // Get any HUB focus areas // These are used where any resource is required to have one of these tags $tconfig = Component::params('com_tags'); $fa1 = $tconfig->get('focus_area_01'); $fa2 = $tconfig->get('focus_area_02'); $fa3 = $tconfig->get('focus_area_03'); $fa4 = $tconfig->get('focus_area_04'); $fa5 = $tconfig->get('focus_area_05'); $fa6 = $tconfig->get('focus_area_06'); $fa7 = $tconfig->get('focus_area_07'); $fa8 = $tconfig->get('focus_area_08'); $fa9 = $tconfig->get('focus_area_09'); $fa10 = $tconfig->get('focus_area_10'); // Instantiate our tag object $tagcloud = new \Components\Resources\Helpers\Tags($row->id); // Normalize the focus areas $tagfa1 = $tagcloud->normalize($fa1); $tagfa2 = $tagcloud->normalize($fa2); $tagfa3 = $tagcloud->normalize($fa3); $tagfa4 = $tagcloud->normalize($fa4); $tagfa5 = $tagcloud->normalize($fa5); $tagfa6 = $tagcloud->normalize($fa6); $tagfa7 = $tagcloud->normalize($fa7); $tagfa8 = $tagcloud->normalize($fa8); $tagfa9 = $tagcloud->normalize($fa9); $tagfa10 = $tagcloud->normalize($fa10); // process new tags if ($tags or $tagfa) { $newtags = ''; if ($tagfa) { $newtags = $tagfa . ', '; } if ($tags) { $newtags .= $tags; } $tagcloud->setTags($newtags, User::get('id')); } // Get all the tags on this resource $tags_men = $tagcloud->tags(); $mytagarray = array(); $fas = array($tagfa1, $tagfa2, $tagfa3, $tagfa4, $tagfa5, $tagfa6, $tagfa7, $tagfa8, $tagfa9, $tagfa10); $fats = array(); if ($fa1) { $fats[$fa1] = $tagfa1; } if ($fa2) { $fats[$fa2] = $tagfa2; } if ($fa3) { $fats[$fa3] = $tagfa3; } if ($fa4) { $fats[$fa4] = $tagfa4; } if ($fa5) { $fats[$fa5] = $tagfa5; } if ($fa6) { $fats[$fa6] = $tagfa6; } if ($fa7) { $fats[$fa7] = $tagfa7; } if ($fa8) { $fats[$fa8] = $tagfa8; } if ($fa9) { $fats[$fa9] = $tagfa9; } if ($fa10) { $fats[$fa10] = $tagfa10; } // Loop through all the tags and pull out the focus areas - those will be displayed differently foreach ($tags_men as $tag_men) { if (in_array($tag_men->get('tag'), $fas)) { $tagfa = $tag_men->get('tag'); } else { $mytagarray[] = $tag_men->get('raw_tag'); } } $tags = implode(', ', $mytagarray); // Set the document title $this->view->title = Lang::txt(strtoupper($this->_option)) . ': ' . Lang::txt('COM_TOOLS_EDIT_TOOL_PAGE') . ' (' . $status['toolname'] . ')'; Document::setTitle($this->view->title); // Set the document pathway (breadcrumbs) if (Pathway::count() <= 0) { Pathway::append(Lang::txt(strtoupper($this->_name)), 'index.php?option=' . $this->_option); } if (Pathway::count() <= 1) { Pathway::append(Lang::txt('COM_TOOLS_STATUS_FOR', $status['toolname']), 'index.php?option=' . $this->_option . '&controller=pipeline&task=status&app=' . $alias); Pathway::append(Lang::txt('COM_TOOLS_EDIT_TOOL_PAGE'), 'index.php?option=' . $this->_option . '&controller=' . $this->_controller . '&app=' . $alias . '&task=start&step=1'); } $this->view->row = $row; $this->view->step = $step; $this->view->version = $version; $this->view->status = $status; $this->view->tags = $tags; $this->view->tagfa = $tagfa; $this->view->fats = $fats; $this->view->authors = $authors; // Pass error messages to the view foreach ($this->getErrors() as $error) { $this->view->setError($error); } // Output HTML $this->view->display(); }
/** * Retrieve an element's options (typically called via AJAX) * * @return void */ public function elementTask() { $ctrl = Request::getVar('ctrl', 'fields'); $option = new stdClass(); $option->label = ''; $option->value = ''; $field = new stdClass(); $field->label = Request::getVar('name', 0); $field->element = ''; $field->description = ''; $field->text = $field->label; $field->name = $field->label; $field->default = ''; $field->type = Request::getVar('type', ''); $field->options = array($option, $option); include_once dirname(dirname(__DIR__)) . DS . 'models' . DS . 'elements.php'; $elements = new \Components\Resources\Models\Elements(); echo $elements->getElementOptions($field->name, $field, $ctrl); }
/** * Saves a resource * Redirects to main listing * * @return void */ public function saveTask() { // Check for request forgeries Request::checkToken(); // Initiate extended database class $row = new Resource($this->database); if (!$row->bind($_POST)) { throw new Exception($row->getError(), 400); } $isNew = 0; if ($row->id < 1) { $isNew = 1; } if ($isNew) { // New entry $row->created = $row->created ? $row->created : Date::toSql(); $row->created_by = $row->created_by ? $row->created_by : User::get('id'); $row->access = 0; } else { $old = new Resource($this->database); $old->load($row->id); $created_by_id = Request::getInt('created_by_id', 0); // Updating entry $row->modified = Date::toSql(); $row->modified_by = User::get('id'); if ($created_by_id) { $row->created_by = $row->created_by ? $row->created_by : $created_by_id; } else { $row->created_by = $row->created_by ? $row->created_by : User::get('id'); } } // publish up $row->publish_up = Date::of($row->publish_up, Config::get('offset'))->toSql(); // publish down if (!$row->publish_down || trim($row->publish_down) == '0000-00-00 00:00:00' || trim($row->publish_down) == 'Never') { $row->publish_down = '0000-00-00 00:00:00'; } else { $row->publish_down = Date::of($row->publish_down, Config::get('offset'))->toSql(); } // Get parameters $params = Request::getVar('params', array(), 'post'); if (is_array($params)) { $txt = new \Hubzero\Config\Registry(''); foreach ($params as $k => $v) { $txt->set($k, $v); } $row->params = $txt->toString(); } // Get attributes $attribs = Request::getVar('attrib', array(), 'post'); if (is_array($attribs)) { $txta = new \Hubzero\Config\Registry(''); foreach ($attribs as $k => $v) { if ($k == 'timeof') { if (strtotime(trim($v)) === false) { $v = NULL; } $v = trim($v) ? Date::of($v, Config::get('offset'))->toSql() : NULL; } $txta->set($k, $v); } $row->attribs = $txta->toString(); } // Get custom areas, add wrappers, and compile into fulltxt if (isset($_POST['nbtag'])) { $type = new Type($this->database); $type->load($row->type); include_once PATH_CORE . DS . 'components' . DS . 'com_resources' . DS . 'models' . DS . 'elements.php'; $elements = new \Components\Resources\Models\Elements(array(), $type->customFields); $schema = $elements->getSchema(); $fields = array(); foreach ($schema->fields as $field) { $fields[$field->name] = $field; } $nbtag = $_POST['nbtag']; $found = array(); foreach ($nbtag as $tagname => $tagcontent) { $f = ''; $row->fulltxt .= "\n" . '<nb:' . $tagname . '>'; if (is_array($tagcontent)) { $c = count($tagcontent); $num = 0; foreach ($tagcontent as $key => $val) { if (trim($val)) { $num++; } $row->fulltxt .= '<' . $key . '>' . trim($val) . '</' . $key . '>'; } if ($c == $num) { $f = 'found'; } } else { $f = trim($tagcontent); if ($f) { $row->fulltxt .= trim($tagcontent); } } $row->fulltxt .= '</nb:' . $tagname . '>' . "\n"; if (!$tagcontent && isset($fields[$tagname]) && $fields[$tagname]->required) { throw new Exception(Lang::txt('RESOURCES_REQUIRED_FIELD_CHECK', $fields[$tagname]->label), 500); } $found[] = $tagname; } foreach ($fields as $field) { if (!in_array($field->name, $found) && $field->required) { $found[] = $field->name; $this->setError(Lang::txt('COM_CONTRIBUTE_REQUIRED_FIELD_CHECK', $field->label)); } } } // Code cleaner for xhtml transitional compliance if ($row->type != 7) { $row->introtext = str_replace('<br>', '<br />', $row->introtext); $row->fulltxt = str_replace('<br>', '<br />', $row->fulltxt); } // Check content if (!$row->check()) { throw new Exception($row->getError(), 500); } // Store content if (!$row->store()) { throw new Exception($row->getError(), 500); } // Checkin resource $row->checkin(); // Rename the temporary upload directory if it exist $tmpid = Request::getInt('tmpid', 0, 'post'); if ($tmpid != Html::niceidformat($row->id)) { // Build the full paths $path = Html::dateToPath($row->created); $dir_id = Html::niceidformat($row->id); $tmppath = Utilities::buildUploadPath($path . DS . $tmpid); $newpath = Utilities::buildUploadPath($path . DS . $dir_id); // Attempt to rename the temp directory if (\Filesystem::exists($tmppath)) { $result = \Filesystem::move($tmppath, $newpath); if ($result !== true) { $this->setError($result); } } $row->path = str_replace($tmpid, Html::niceidformat($row->id), $row->path); $row->store(); } // Incoming tags $tags = Request::getVar('tags', '', 'post'); // Save the tags $rt = new Tags($row->id); $rt->setTags($tags, User::get('id'), 1, 1); // Incoming authors if ($row->type != 7) { $authorsOldstr = Request::getVar('old_authors', '', 'post'); $authorsNewstr = Request::getVar('new_authors', '', 'post'); if (!$authorsNewstr) { $authorsNewstr = $authorsOldstr; } include_once dirname(dirname(__DIR__)) . DS . 'tables' . DS . 'contributor.php'; $authorsNew = explode(',', $authorsNewstr); $authorsOld = explode(',', $authorsOldstr); // We have either a new ordering or new authors or both if ($authorsNewstr) { for ($i = 0, $n = count($authorsNew); $i < $n; $i++) { $rc = new Contributor($this->database); $rc->subtable = 'resources'; $rc->subid = $row->id; if (is_numeric($authorsNew[$i])) { $rc->authorid = $authorsNew[$i]; } else { $rc->authorid = $rc->getUserId($authorsNew[$i]); } $rc->ordering = $i; $rc->role = trim(Request::getVar($authorsNew[$i] . '_role', '')); $rc->name = trim(Request::getVar($authorsNew[$i] . '_name', '')); $rc->organization = trim(Request::getVar($authorsNew[$i] . '_organization', '')); $authorsNew[$i] = $rc->authorid; if (in_array($authorsNew[$i], $authorsOld)) { //echo 'update: ' . $rc->authorid . ', ' . $rc->role . ', ' . $rc->name . ', ' . $rc->organization . '<br />'; // Updating record $rc->updateAssociation(); } else { //echo 'create: ' . $rc->authorid . ', ' . $rc->role . ', ' . $rc->name . ', ' . $rc->organization . '<br />'; // New record $rc->createAssociation(); } } } // Run through previous author list and check to see if any IDs had been dropped if ($authorsOldstr) { $rc = new Contributor($this->database); for ($i = 0, $n = count($authorsOld); $i < $n; $i++) { if (!in_array($authorsOld[$i], $authorsNew)) { $rc->deleteAssociation($authorsOld[$i], $row->id, 'resources'); } } } } // If this is a child, add parent/child association $pid = Request::getInt('pid', 0, 'post'); if ($isNew && $pid) { $this->_attachChild($row->id, $pid); } // Is this a standalone resource and we need to email approved submissions? if ($row->standalone == 1 && $this->config->get('email_when_approved')) { // If the state went from pending to published if ($row->published == 1 && $old->published == 3) { $this->_emailContributors($row, $this->database); } } // Redirect App::redirect($this->buildRedirectURL($pid), Lang::txt('COM_RESOURCES_ITEM_SAVED')); }
} ?> </fieldset> <?php if ($this->row->standalone == 1 && $this->row->type != 7) { ?> <fieldset class="adminform"> <legend><span><?php echo Lang::txt('Custom fields'); ?> </span></legend> <div class="input-wrap" id="resource-custom-fields"> <?php $elements = new \Components\Resources\Models\Elements($data, $type->customFields); echo $elements->render(); ?> </div> </fieldset> <?php } ?> </div> <div class="col span5"> <?php if ($this->row->id) { ?> <table class="meta"> <tbody> <tr>
<tfoot> <tr> <td colspan="5"> <a class="button" id="add-custom-field" href="#addRow"> <span><?php echo Lang::txt('COM_RESOURCES_NEW_ROW'); ?> </span> </a> </td> </tr> </tfoot> <tbody id="field-items"> <?php include_once PATH_CORE . DS . 'components' . DS . 'com_resources' . DS . 'models' . DS . 'elements.php'; $elements = new \Components\Resources\Models\Elements('', $this->row->customFields); $schema = $elements->getSchema(); if (!is_object($schema)) { $schema = new stdClass(); $schema->fields = array(); } if (count($schema->fields) <= 0) { $fs = explode(',', $this->config->get('tagsothr', 'bio,credits,citations,sponsoredby,references,publications')); foreach ($fs as $f) { $f = trim($f); $element = new stdClass(); $element->name = preg_replace('/[^a-zA-Z0-9]/', '', strtolower($f)); $element->label = ucfirst($f); $element->type = 'text'; $element->required = ''; $element->value = '';
$this->model->resource->fulltxt = stripslashes($this->model->resource->fulltxt); $this->model->resource->fulltxt = $this->model->resource->fulltxt ? trim($this->model->resource->fulltxt) : trim($this->model->resource->introtext); // Parse for <nb:field> tags $type = new \Components\Resources\Tables\Type($this->database); $type->load($this->model->resource->type); $data = array(); preg_match_all("#<nb:(.*?)>(.*?)</nb:(.*?)>#s", $this->model->resource->fulltxt, $matches, PREG_SET_ORDER); if (count($matches) > 0) { foreach ($matches as $match) { $data[$match[1]] = $match[2]; } } $this->model->resource->fulltxt = preg_replace("#<nb:(.*?)>(.*?)</nb:(.*?)>#s", '', $this->model->resource->fulltxt); $this->model->resource->fulltxt = trim($this->model->resource->fulltxt); include_once PATH_CORE . DS . 'components' . DS . 'com_resources' . DS . 'models' . DS . 'elements.php'; $elements = new \Components\Resources\Models\Elements($data, $this->model->type->customFields); $schema = $elements->getSchema(); // Set the document description if ($this->model->resource->introtext) { Document::setDescription(strip_tags($this->model->resource->introtext)); } // Check if there's anything left in the fulltxt after removing custom fields // If not, set it to the introtext $maintext = $this->model->description('parsed'); ?> <div class="subject abouttab"> <?php if ($this->model->isTool()) { ?> <?php if ($this->model->resource->revision == 'dev' or !$this->model->resource->toolpublished) {
defined('_HZEXEC_') or die; $database = App::get('db'); $this->status['fulltxt'] = stripslashes($this->status['fulltxt']); $type = new \Components\Resources\Tables\Type($database); $type->load(7); $data = array(); preg_match_all("#<nb:(.*?)>(.*?)</nb:(.*?)>#s", $this->status['fulltxt'], $matches, PREG_SET_ORDER); if (count($matches) > 0) { foreach ($matches as $match) { $data[$match[1]] = trim($match[2]); } } $this->status['fulltxt'] = preg_replace("#<nb:(.*?)>(.*?)</nb:(.*?)>#s", '', $this->status['fulltxt']); $this->status['fulltxt'] = trim($this->status['fulltxt']); include_once PATH_CORE . DS . 'components' . DS . 'com_resources' . DS . 'models' . DS . 'elements.php'; $elements = new \Components\Resources\Models\Elements($data, $type->customFields); $fields = $elements->render(); ?> <div class="explaination"> <p class="help"><?php echo $this->dev ? Lang::txt('COM_TOOLS_SIDE_EDIT_PAGE') : Lang::txt('COM_TOOLS_SIDE_EDIT_PAGE_CURRENT'); ?> </p> <p><?php echo Lang::txt('COM_TOOLS_COMPOSE_ABSTRACT_HINT'); ?> </p> </div> <fieldset> <legend><?php echo Lang::txt('COM_TOOLS_COMPOSE_ABOUT');