Index: core/kernel/application.php
===================================================================
--- core/kernel/application.php
+++ core/kernel/application.php
@@ -733,6 +733,7 @@
 		$this->registerClass('kCatDBTagProcessor', KERNEL_PATH . '/db/cat_tag_processor.php');
 		$this->registerClass('NParser', KERNEL_PATH . '/nparser/nparser.php');
 		$this->registerClass('TemplatesCache', KERNEL_PATH . '/nparser/template_cache.php');
+		$this->registerClass('TemplateCommentParser', KERNEL_PATH . '/utility/template_comment_parser.php');
 
 		// database
 		$this->registerClass('kDBConnection', KERNEL_PATH . '/db/db_connection.php');
Index: core/kernel/utility/template_comment_parser.php
===================================================================
--- /dev/null
+++ core/kernel/utility/template_comment_parser.php
@@ -0,0 +1,163 @@
+<?php
+/**
+ * Template comment parser
+ *
+ * @version	  $Id$
+ * @package	  In-Portal
+ * @copyright Copyright (C) 1997 - 2015 Intechnic. All rights reserved.
+ * @license   GNU/GPL
+ * In-Portal is Open Source software.
+ * This means that this software may have been modified pursuant
+ * the GNU General Public License, and as distributed it includes
+ * or is derivative of works licensed under the GNU General Public License
+ * or other free or open source software licenses.
+ * See http://www.in-portal.org/license for copyright notices and details.
+ */
+
+defined('FULL_PATH') or die('restricted access!');
+
+class TemplateCommentParser
+{
+
+	/**
+	 * Path to template
+	 *
+	 * @var string
+	 */
+	protected $templateFile;
+
+	/**
+	 * Supported settings
+	 *
+	 * @var array
+	 */
+	protected $supportedSettings = array(
+		'NAME' => array('type' => 'string', 'default' => ''),
+		'DESC' => array('type' => 'string', 'default' => ''),
+		'SECTION' => array('type' => 'category_path', 'default' => ''),
+	);
+
+	/**
+	 * Gets and checks path to template
+	 *
+	 * @param string $template_file Template file.
+	 */
+	public function __construct($template_file)
+	{
+		$this->templateFile = $template_file;
+	}
+
+	/**
+	 * Parses template comment
+	 *
+	 * @return array
+	 * @throws Exception When template comment can't be parsed.
+	 */
+	public function parse()
+	{
+		if ( !file_exists($this->templateFile) ) {
+			throw new Exception('Template file "' . $this->templateFile . '" not found.');
+		}
+
+		$result = $this->getDefaults();
+		$template_data = file_get_contents($this->templateFile);
+
+		if ( substr($template_data, 0, 6) != '<!--##' ) {
+			return $result;
+		}
+
+		// template starts with comment in such format
+		/*<!--##
+		<NAME></NAME>
+		<DESC></DESC>
+		<SECTION>||</SECTION>
+		##-->*/
+
+		$comment_end = strpos($template_data, '##-->');
+
+		if ( $comment_end === false ) {
+			throw new Exception('Template file "' . $this->templateFile . '" missing end of comment.');
+		}
+
+		$comment = trim(substr($template_data, 6, $comment_end - 6));
+
+		if ( strpos($comment, PHP_EOL) === false ) {
+			return $result;
+		}
+
+		$xml_parser = xml_parser_create();
+		$is_valid = xml_parse_into_struct($xml_parser, '<comment>' . $comment . '</comment>', $values, $index);
+		xml_parser_free($xml_parser);
+		$exception_message_start = 'Template file "' . $this->templateFile . '" have ';
+
+		if ( !$is_valid ) {
+			throw new Exception($exception_message_start . 'invalid XML in the comment.');
+		}
+
+		array_shift($index);
+
+		foreach ( $index as $setting => $section_index ) {
+			if ( !isset($this->supportedSettings[$setting]) ) {
+				throw new Exception($exception_message_start . 'unsupported setting "' . $setting . '".');
+			}
+
+			$result_key = strtolower($setting);
+
+			if ( isset($values[$section_index[0]]['value']) ) {
+				$result[$result_key] = $values[$section_index[0]]['value'];
+			}
+
+			switch ( $this->supportedSettings[$setting]['type'] ) {
+				case 'category_path':
+					$category_path = explode('||', $result[$result_key]);
+					$category_path = array_map('trim', $category_path);
+					$result[$result_key] = implode('||', $category_path);
+
+					break;
+				case 'integer':
+					$parsed = (int)$result[$result_key];
+
+					if ( (string)$parsed != $result[$result_key] ) {
+						throw new Exception(
+							$exception_message_start . ' must have integer value in setting "' . $setting . '".'
+						);
+					}
+
+					if ( $parsed < 1 ) {
+						throw new Exception(
+							$exception_message_start . ' may have only positive value in setting "' . $setting . '".'
+						);
+					}
+					break;
+
+				case 'boolean':
+					if ( $result[$result_key] !== 'yes' && $result[$result_key] !== 'no' ) {
+						throw new Exception(
+							$exception_message_start . ' must have "yes" or "no" value in setting "' . $setting . '".'
+						);
+					}
+
+					$result[$result_key] = $result[$result_key] == 'yes';
+			}
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Get default values
+	 *
+	 * @return array
+	 */
+	protected function getDefaults()
+	{
+		$result = array();
+
+		foreach ( $this->supportedSettings as $setting => $options ) {
+			$result[strtolower($setting)] = $options['default'];
+		}
+
+		return $result;
+	}
+
+}
Index: core/units/helpers/themes_helper.php
===================================================================
--- core/units/helpers/themes_helper.php
+++ core/units/helpers/themes_helper.php
@@ -376,7 +376,7 @@
 				elseif ( pathinfo($filename, PATHINFO_EXTENSION) == 'tpl' ) {
 					$meta_info = $this->_getTemplateMetaInfo(trim($file_path, '/'), $theme_id);
 					$file_id = isset($this->themeFiles[$file_path]) ? $this->themeFiles[$file_path] : false;
-					$file_description = array_key_exists('desc', $meta_info) ? $meta_info['desc'] : '';
+					$file_description = $meta_info['desc'];
 
 					if ($file_id) {
 						// file was found in db & on hdd -> mark as existing
@@ -449,51 +449,9 @@
 
 			$template = 'theme:' . $this->_themeNames[$theme_id] . '/' . $template;
 			$template_file = $this->Application->TemplatesCache->GetRealFilename($template); // ".tpl" was added before
+			$template_comment_parser = $this->Application->makeClass('TemplateCommentParser', array($template_file));
 
-			return $this->parseTemplateMetaInfo($template_file);
-		}
-
-		function parseTemplateMetaInfo($template_file)
-		{
-			if (!file_exists($template_file)) {
-				// when template without info it's placed in top category
-				return Array ();
-			}
-
-			$template_data = file_get_contents($template_file);
-
-			if (substr($template_data, 0, 6) == '<!--##') {
-				// template starts with comment in such format
-				/*<!--##
-				<NAME></NAME>
-				<DESC></DESC>
-				<SECTION>||</SECTION>
-				##-->*/
-
-				$comment_end = strpos($template_data, '##-->');
-				if ($comment_end === false) {
-					// badly formatted comment
-					return Array ();
-				}
-
-				$comment = trim( substr($template_data, 6, $comment_end - 6) );
-				if (preg_match_all('/<(NAME|DESC|SECTION)>(.*?)<\/(NAME|DESC|SECTION)>/is', $comment, $regs)) {
-					$ret = Array ();
-					foreach ($regs[1] as $param_order => $param_name) {
-						$ret[ strtolower($param_name) ] = trim($regs[2][$param_order]);
-					}
-
-					if (array_key_exists('section', $ret) && $ret['section']) {
-						$category_path = explode('||', $ret['section']);
-						$category_path = array_map('trim', $category_path);
-						$ret['section'] = implode('||', $category_path);
-					}
-
-					return $ret;
-				}
-			}
-
-			return Array ();
+			return $template_comment_parser->parse();
 		}
 
 		/**
Index: core/units/theme_files/theme_file_eh.php
===================================================================
--- core/units/theme_files/theme_file_eh.php
+++ core/units/theme_files/theme_file_eh.php
@@ -119,11 +119,9 @@
 				fwrite($fp, $object->GetDBField('FileContents'));
 				fclose($fp);
 
-				$themes_helper = $this->Application->recallObject('ThemesHelper');
-				/* @var $themes_helper kThemesHelper */
-
-				$meta_info = $themes_helper->parseTemplateMetaInfo($filename);
-				$file_description = array_key_exists('desc', $meta_info) ? $meta_info['desc'] : '';
+				$template_comment_parser = $this->Application->makeClass('TemplateCommentParser', array($filename));
+				$meta_info = $template_comment_parser->parse();
+				$file_description = $meta_info['desc'];
 
 				$object->SetDBField('Description', $file_description);
 				$object->SetDBField('FileMetaInfo', serialize($meta_info));