Commit 596c2e02e09405043b0c2c92d95d7d9c92f72c98

Authored by Brad Shuttleworth
1 parent 37b14654

made help work across multiple languages (KTS-662)


git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@5427 c91229c3-7414-0410-bfa2-8a42b809f60b
HISTORY.txt
@@ -10,6 +10,7 @@ Features: @@ -10,6 +10,7 @@ Features:
10 10
11 Fixes: 11 Fixes:
12 12
  13 + - Made help able to work across multiple languages
13 - KTS-972 Owner, Everyone and Authenticated weren't being respected by browse 14 - KTS-972 Owner, Everyone and Authenticated weren't being respected by browse
14 and search 15 and search
15 - KTS-959 Documents weren't properly purged from the cache on change. 16 - KTS-959 Documents weren't properly purged from the cache on change.
help.php
@@ -71,20 +71,76 @@ class HelpDispatcher extends KTStandardDispatcher { @@ -71,20 +71,76 @@ class HelpDispatcher extends KTStandardDispatcher {
71 function do_main() { 71 function do_main() {
72 // store referer 72 // store referer
73 $sBackKey = KTUtil::arrayGet($_REQUEST, 'back_key', false); 73 $sBackKey = KTUtil::arrayGet($_REQUEST, 'back_key', false);
  74 + $sSubPath = KTUtil::arrayGet($_SERVER, 'PATH_INFO');
  75 +
  76 + // we want to be able to say "i left the system at point x. go back there"
74 if(!$sBackKey) { 77 if(!$sBackKey) {
75 $sReferer = KTUtil::arrayGet($_SERVER ,'HTTP_REFERER'); 78 $sReferer = KTUtil::arrayGet($_SERVER ,'HTTP_REFERER');
76 $sBackKey = KTUtil::randomString(); 79 $sBackKey = KTUtil::randomString();
77 $_SESSION[$sBackKey] = $sReferer; 80 $_SESSION[$sBackKey] = $sReferer;
78 } 81 }
79 82
80 - $pathinfo = KTUtil::arrayGet($_SERVER, 'PATH_INFO');  
81 - if (empty($pathinfo)) { 83 + // no path specified
  84 + if (empty($sSubPath)) {
82 $this->oPage->setTitle(_kt('No help page specified.')); 85 $this->oPage->setTitle(_kt('No help page specified.'));
83 $this->oPage->addError(_kt('No help page specified.')); 86 $this->oPage->addError(_kt('No help page specified.'));
84 return ' '; 87 return ' ';
85 } 88 }
86 89
87 - $can_edit = Permission::userIsSystemAdministrator($_SESSION['userID']); 90 + // simple test to see if this user is active.
  91 + $bCanEdit = Permission::userIsSystemAdministrator($_SESSION['userID']);
  92 +
  93 + global $default;
  94 + $sLangCode = $default->defaultLanguage;
  95 + /*
  96 + now we need to know a few things.
  97 + 1. can we find this help file?
  98 + 2. if we can, display it
  99 + 2.1 images directly
  100 + 2.2 html wrapped.
  101 + 3. if now, fail out.
  102 +
  103 + this is essentially handled by asking help.inc.php for the
  104 + subpath we've been given, PLUS THE LANGUAGE, and checking for
  105 + a PEAR::raiseError.
  106 +
  107 + The "Correct" response we care about is a dictionary:
  108 +
  109 + {
  110 + 'is_image': string
  111 + 'title': string
  112 + 'body': string
  113 + }
  114 + */
  115 +
  116 + $aHelpData = KTHelp::getHelpInfo($sSubPath);
  117 +
  118 + if (PEAR::isError($aHelpData)) {
  119 + $this->oPage->setTitle($aHelpData->getMessage());
  120 + $this->oPage->addError($aHelpData->getMessage());
  121 + return '&nbsp';
  122 + }
  123 +
  124 + $aLocInfo = KTHelp::_getLocationInfo($sSubPath);
  125 +
  126 + if ($aHelpData['is_image']) {
  127 + KTHelp::outputHelpImage($sSubPath);
  128 + exit(0); // done.
  129 + } else {
  130 + $this->oPage->setTitle($aHelpData['title']);
  131 + $this->aBreadcrumbs[] = array('url' => $_SERVER['PHP_SELF'], 'name' => $aHelpData['title']);
  132 + $oTemplating =& KTTemplating::getSingleton();
  133 + $oTemplate = $oTemplating->loadTemplate("ktcore/help_with_edit");
  134 + $aTemplateData = array(
  135 + "context" => $this,
  136 + "help_body" => $aHelpData['body'],
  137 + "target_name" => KTUtil::arrayGet($aLocInfo, 'internal'),
  138 + "back_key" => $sBackKey,
  139 + 'can_edit' => $bCanEdit,
  140 + );
  141 + return $oTemplate->render($aTemplateData);
  142 + }
  143 + /*
88 $help_path = KTHelp::getHelpSubPath($pathinfo); 144 $help_path = KTHelp::getHelpSubPath($pathinfo);
89 145
90 if ($help_path == false) { 146 if ($help_path == false) {
@@ -100,7 +156,7 @@ class HelpDispatcher extends KTStandardDispatcher { @@ -100,7 +156,7 @@ class HelpDispatcher extends KTStandardDispatcher {
100 KTHelp::outputHelpImage($help_path); 156 KTHelp::outputHelpImage($help_path);
101 } else { 157 } else {
102 // not an image, so: 158 // not an image, so:
103 - $aHelpInfo = KTHelp::getHelpFromFile($pathinfo); 159 + $aHelpInfo = KTHelp::getHelpFromFile($pathinfo)
104 } 160 }
105 161
106 162
@@ -129,16 +185,7 @@ class HelpDispatcher extends KTStandardDispatcher { @@ -129,16 +185,7 @@ class HelpDispatcher extends KTStandardDispatcher {
129 $this->oPage->setTitle($aHelpInfo['title']); 185 $this->oPage->setTitle($aHelpInfo['title']);
130 $this->aBreadcrumbs[] = array('url' => $_SERVER['PHP_SELF'], 'name' => $aHelpInfo['title']); 186 $this->aBreadcrumbs[] = array('url' => $_SERVER['PHP_SELF'], 'name' => $aHelpInfo['title']);
131 187
132 - $oTemplating =& KTTemplating::getSingleton();  
133 - $oTemplate = $oTemplating->loadTemplate("ktcore/help_with_edit");  
134 - $aTemplateData = array(  
135 - "context" => $this,  
136 - "help_body" => $aHelpInfo['body'],  
137 - "target_name" => $_SERVER['PATH_INFO'],  
138 - "back_key" => $sBackKey,  
139 - 'can_edit' => $can_edit,  
140 - );  
141 - return $oTemplate->render($aTemplateData); 188 + */
142 } 189 }
143 190
144 function do_go_back() { 191 function do_go_back() {
lib/help/help.inc.php
@@ -31,64 +31,7 @@ @@ -31,64 +31,7 @@
31 require_once(KT_LIB_DIR . "/database/dbutil.inc"); 31 require_once(KT_LIB_DIR . "/database/dbutil.inc");
32 32
33 class KTHelp { 33 class KTHelp {
34 -  
35 - function getHelpSubPath($sHelpFile) {  
36 - if (empty($sHelpFile)) { return false; }  
37 - $path_segments = explode("/", $sHelpFile);  
38 - // cannot be empty, must contain at _least_ 1 item.  
39 - if (empty($path_segments[0])) {  
40 - $path_segments = array_slice($path_segments,1);  
41 - }  
42 -  
43 - if (empty($path_segments) or (count($path_segments) < 2)) {  
44 - return false; // FIXME use PEAR::Error  
45 - }  
46 -  
47 - // we now assume that path_segments[0] is the module  
48 - // path_segments[1..] is the subpath. we need to insert the LANG  
49 -  
50 - $lang_code = 'EN'; // FIXME extract the lang from the environ (?)  
51 -  
52 - $final_path = array(null,'kthelp', $path_segments[0]);  
53 - $final_path[] = $lang_code;  
54 - $final_path = kt_array_merge($final_path, array_slice($path_segments, 1));  
55 -  
56 - $help_path = implode('/',$final_path);  
57 -  
58 - return $help_path;  
59 - }  
60 -  
61 - function getHelpFromFile($sHelpFile) {  
62 - if (empty($sHelpFile)) { return false; }  
63 - $help_path = KTHelp::getHelpSubPath($sHelpFile);  
64 -  
65 - $fspath = KT_DIR . $help_path; // FIXME use OS.path_sep equivalent?  
66 -  
67 - if (!file_exists($fspath)) {  
68 - return false;  
69 - }  
70 -  
71 - if (KTHelp::isImageFile($help_path)) {  
72 - return false; // can't - not what users expect.  
73 - }  
74 -  
75 - // now we ASSUME its html: we'll fail anyway if we aren't.  
76 - $handle = fopen($fspath, "r");  
77 - $contents = fread($handle, filesize($fspath));  
78 - fclose($handle);  
79 -  
80 - $info = KTHelp::_parseHTML($contents);  
81 -  
82 - $body = KTUtil::arrayGet($info,'body');  
83 - if (empty($body)) {  
84 - return false;  
85 - }  
86 -  
87 - $info['name'] = $help_path; // set so we can save into db if needed.  
88 -  
89 - return $info;  
90 - }  
91 - 34 +
92 // world's simplest (and possibly worst) regex-split. 35 // world's simplest (and possibly worst) regex-split.
93 function _parseHTML($sHTML) { 36 function _parseHTML($sHTML) {
94 $title_array = preg_split('#</?title>#',$sHTML,-1,PREG_SPLIT_NO_EMPTY); 37 $title_array = preg_split('#</?title>#',$sHTML,-1,PREG_SPLIT_NO_EMPTY);
@@ -107,11 +50,10 @@ class KTHelp { @@ -107,11 +50,10 @@ class KTHelp {
107 return $res; 50 return $res;
108 } 51 }
109 52
110 - function isImageFile($sHelpPath) {  
111 - // from pluginutil.inc.php  
112 - $fspath = KT_DIR . $sHelpPath;  
113 -  
114 - $pi = pathinfo($fspath); 53 + // $sFSPath : filesystem path for the resource.
  54 + // return true or false
  55 + function isImageFile($sFSPath) {
  56 + $pi = pathinfo($sFSPath);
115 $mime_type = ""; 57 $mime_type = "";
116 $sExtension = KTUtil::arrayGet($pi, 'extension'); 58 $sExtension = KTUtil::arrayGet($pi, 'extension');
117 if (!empty($sExtension)) { 59 if (!empty($sExtension)) {
@@ -125,9 +67,18 @@ class KTHelp { @@ -125,9 +67,18 @@ class KTHelp {
125 return false; 67 return false;
126 } 68 }
127 69
128 - function outputHelpImage($sHelpPath) {  
129 - $fspath = KT_DIR . $sHelpPath;  
130 - 70 + // output the help image referred to by the subpath
  71 + function outputHelpImage($sSubPath) {
  72 + // FIXME there are error cases here ...
  73 + $aPathInfo = KTHelp::_getLocationInfo($sSubPath);
  74 + if (PEAR::isError($aPathInfo)) { return $aPathInfo; } // gets caught further up the stack
  75 +
  76 + $pi = pathinfo($aPathInfo['external']);
  77 + $mime_type = "";
  78 + $sExtension = KTUtil::arrayGet($pi, 'extension');
  79 + if (!empty($sExtension)) {
  80 + $mime_type = DBUtil::getOneResultKey(array("SELECT mimetypes FROM " . KTUtil::getTableName('mimetypes') . " WHERE LOWER(filetypes) = ?", $sExtension), "mimetypes");
  81 + }
131 82
132 header("Content-Type: $mime_type"); 83 header("Content-Type: $mime_type");
133 header("Content-Length: " . filesize($fspath)); 84 header("Content-Length: " . filesize($fspath));
@@ -135,6 +86,144 @@ class KTHelp { @@ -135,6 +86,144 @@ class KTHelp {
135 exit(0); 86 exit(0);
136 } 87 }
137 88
  89 + /*
  90 + input:
  91 + sSubPath : the path in the form "plugin/lower-level-file"
  92 + sLangCode : the language code in the form "en_US"
  93 +
  94 + returns a dictionary
  95 + {
  96 + 'is_image': string
  97 + 'title': string
  98 + 'body': string
  99 + }
  100 + */
  101 + function getHelpInfo($sSubPath, $sLangCode = null) {
  102 + $aInfo = array(
  103 + 'is_image' => false,
  104 + 'title' => null,
  105 + 'body' => null,
  106 + 'help_id' => null,
  107 + );
  108 + $aPathInfo = KTHelp::_getLocationInfo($sSubPath, $sLangCode);
  109 + if (PEAR::isError($aPathInfo)) {
  110 + return $aPathInfo;
  111 + }
  112 +
  113 + // first, check the extension to see if its an image.
  114 + // failing that, check the DB for an entry (+ return if found)
  115 + // failing that, check the FS for the entry (+ return if found)
  116 + // failing that, through an exception.
  117 +
  118 + if (KTHelp::isImageFile($aPathInfo['external'])) {
  119 + $aInfo['is_image'] = true;
  120 + return $aInfo;
  121 + }
  122 +
  123 + // check DB
  124 + $oReplacement =& KTHelpReplacement::getByName($aPathInfo['internal']);
  125 + if (!PEAR::isError($oReplacement)) {
  126 + $aInfo['title'] = $oReplacement->getTitle();
  127 + $aInfo['body'] = $oReplacement->getDescription();
  128 + $aInfo['help_id'] = $oReplacement->getID();
  129 + return $aInfo;
  130 + }
  131 +
  132 + // check FS
  133 + if (!file_exists($aPathInfo['external'])) {
  134 + return PEAR::raiseError(_kt("Unable to locate the requested help file for this language."));
  135 + }
  136 +
  137 + // so it might exist in some form.
  138 + $contents = file_get_contents($aPathInfo['external']);
  139 +
  140 + $aData = KTHelp::_parseHTML($contents);
  141 +
  142 + $aInfo['body'] = KTUtil::arrayGet($aData,'body');
  143 + if (empty($aInfo['body'])) {
  144 + return PEAR::raiseError(_kt("The requested help language has no contents."));
  145 + }
  146 + $aInfo['title'] = KTUtil::arrayGet($aData, 'title', _kt("Untitled Help File"));
  147 +
  148 + return $aInfo;
  149 + }
  150 +
  151 + function _getLocationInfo($sSubPath, $sLangCode = null) {
  152 + // FIXME use a cheap cache here? is it even worth it?
  153 + $aInfo = array(
  154 + 'subpath' => null,
  155 + 'internal' => null,
  156 + 'external' => null,
  157 + );
  158 +
  159 + $oHelpReg =& KTHelpRegistry::getSingleton();
  160 +
  161 + if (is_null($sLangCode)) {
  162 + global $default;
  163 + $sLangCode = $default->defaultLanguage;
  164 + }
  165 +
  166 + $aParts = explode('/', $sSubPath);
  167 +
  168 + if (count($aParts) < 2) {
  169 + return PEAR::raiseError(_kt("Too few parts to the requested help location."));
  170 + }
  171 +
  172 + $sPluginName = $aParts[0];
  173 + $sSubLocation = implode('/', array_slice($aParts, 1));
  174 +
  175 + $sInternalName = sprintf("%s/%s/%s", $sPluginName, $sLangCode, $sSubLocation);
  176 +
  177 + // this is a pseudo-name. essentially, this maps to the canonical
  178 + // name of the help file in the database, NOT to the filesystem
  179 +
  180 + //$sBaseDir = sprintf("%s/kthelp/%s/%s", KT_DIR, $sPluginName, $sLangCode);
  181 + $sBaseDir = $oHelpReg->getBaseDir($sPluginName, $sLangCode);
  182 +
  183 + if (PEAR::isError($sBaseDir)) { return $sBaseDir; }
  184 + $sExternalName = sprintf("%s/%s", $sBaseDir, $sSubLocation);
  185 +
  186 + $aInfo['subpath'] = $sSubPath;
  187 + $aInfo['internal'] = $sInternalName;
  188 + $aInfo['external'] = $sExternalName;
  189 +
  190 + return $aInfo;
  191 + }
  192 +}
  193 +
  194 +class KTHelpRegistry {
  195 + var $plugin_lang_map;
  196 +
  197 + function KTHelpRegistry() {
  198 + $this->plugin_lang_map = array();
  199 + }
  200 +
  201 + function &getSingleton () {
  202 + if (!KTUtil::arrayGet($GLOBALS, 'oKTHelpRegistry')) {
  203 + $GLOBALS['oKTHelpRegistry'] = new KTHelpRegistry;
  204 + }
  205 +
  206 + return $GLOBALS['oKTHelpRegistry'];
  207 + }
  208 +
  209 + function registerHelp($sPluginName, $sLang, $sBaseDir) {
  210 + $lang_map = KTUtil::arrayGet($this->plugin_lang_map, $sPluginName, array());
  211 + $lang_map[$sLang] = $sBaseDir;
  212 + $this->plugin_lang_map[$sPluginName] = $lang_map;
  213 + }
  214 +
  215 + function getBaseDir($sPluginName, $sLangCode) {
  216 + $lang_map = KTUtil::arrayGet($this->plugin_lang_map, $sPluginName);
  217 +
  218 + if (is_null($lang_map)) {
  219 + return PEAR::raiseError(_kt("There is no help available in your language for this plugin"));
  220 + }
  221 + $sBaseDir = KTUtil::arrayGet($lang_map, $sLangCode);
  222 + if (is_null($sBaseDir)) {
  223 + return PEAR::raiseError(_kt("There is no help available in your language for this plugin"));
  224 + }
  225 + return $sBaseDir;
  226 + }
138 } 227 }
139 228
140 ?> 229 ?>
plugins/ktcore/KTDashlets.php
@@ -46,15 +46,13 @@ class KTInfoDashlet extends KTBaseDashlet { @@ -46,15 +46,13 @@ class KTInfoDashlet extends KTBaseDashlet {
46 $aHelpInfo = array(); 46 $aHelpInfo = array();
47 $can_edit = Permission::userIsSystemAdministrator($_SESSION['userID']); 47 $can_edit = Permission::userIsSystemAdministrator($_SESSION['userID']);
48 48
49 - $help_path = KTHelp::getHelpSubPath($this->helpLocation);  
50 - if ($help_path == false) { 49 + $help_path = KTHelp::_getLocationInfo($this->helpLocation);
  50 + if (PEAR::isError($help_path)) {
51 return false; 51 return false;
52 } 52 }
53 53
54 // We now check for substitute help files. try to generate an error. 54 // We now check for substitute help files. try to generate an error.
55 - $oReplacementHelp = KTHelpReplacement::getByName($help_path);  
56 -  
57 - $aHelpInfo = KTHelp::getHelpFromFile($this->helpLocation); 55 + $aHelpInfo = KTHelp::getHelpInfo($this->helpLocation);
58 56
59 // NORMAL users never see edit-option. 57 // NORMAL users never see edit-option.
60 if (!$can_edit) { 58 if (!$can_edit) {
@@ -67,19 +65,6 @@ class KTInfoDashlet extends KTBaseDashlet { @@ -67,19 +65,6 @@ class KTInfoDashlet extends KTBaseDashlet {
67 } 65 }
68 } 66 }
69 67
70 -  
71 -  
72 - if (!PEAR::isError($oReplacementHelp)) {  
73 - $aHelpInfo['title'] = $oReplacementHelp->getTitle();  
74 - $aHelpInfo['body'] = $oReplacementHelp->getDescription();  
75 - $this->help_id = $oReplacementHelp->getId();  
76 -  
77 - } else {  
78 - $this->help_id = null;  
79 - }  
80 -  
81 - if (empty($aHelpInfo)) { return false; }  
82 -  
83 $this->aHelpInfo = $aHelpInfo; 68 $this->aHelpInfo = $aHelpInfo;
84 $this->canEdit = $can_edit; 69 $this->canEdit = $can_edit;
85 70
@@ -97,7 +82,7 @@ class KTInfoDashlet extends KTBaseDashlet { @@ -97,7 +82,7 @@ class KTInfoDashlet extends KTBaseDashlet {
97 'body' => $this->aHelpInfo['body'], 82 'body' => $this->aHelpInfo['body'],
98 'can_edit' => $this->canEdit, 83 'can_edit' => $this->canEdit,
99 'target_name' => $this->helpLocation, 84 'target_name' => $this->helpLocation,
100 - 'help_id' => $this->help_id, 85 + 'help_id' => $this->aHelpInfo['help_id'],
101 ); 86 );
102 return $oTemplate->render($aTemplateData); 87 return $oTemplate->render($aTemplateData);
103 } 88 }
plugins/ktcore/admin/manageHelp.php
@@ -126,19 +126,19 @@ class ManageHelpDispatcher extends KTAdminDispatcher { @@ -126,19 +126,19 @@ class ManageHelpDispatcher extends KTAdminDispatcher {
126 126
127 function do_customise() { 127 function do_customise() {
128 $name = KTUtil::arrayGet($_REQUEST, 'name'); 128 $name = KTUtil::arrayGet($_REQUEST, 'name');
129 - $subname = KTHelp::getHelpSubPath($name);  
130 - $oHelpReplacement = KTHelpReplacement::getByName($subname); 129 + $aPathInfo = KTHelp::_getLocationInfo($name);
  130 + $oHelpReplacement = KTHelpReplacement::getByName($aPathInfo['internal']);
131 // XXX: Check against "already exists" 131 // XXX: Check against "already exists"
132 132
133 //var_dump($name); 133 //var_dump($name);
134 134
135 if (!PEAR::isError($oHelpReplacement)) { 135 if (!PEAR::isError($oHelpReplacement)) {
136 // Already exists... 136 // Already exists...
137 - return $this->errorRedirectTo('editReplacement', _kt('Replacement already exists.'),'id=' . $oHelpReplacement->getId()); 137 + return $this->successRedirectTo('editReplacement', _kt('Replacement already exists. Editing the existing copy instead of replacing.'),'id=' . $oHelpReplacement->getId());
138 } 138 }
139 139
140 - $info = KTHelp::getHelpFromFile($name);  
141 - if ($info === false) { 140 + $info = KTHelp::getHelpInfo($name);
  141 + if (PEAR::isError($info)) {
142 $info = array('name' => $name); 142 $info = array('name' => $name);
143 $info['title'] = _kt('New Help File'); 143 $info['title'] = _kt('New Help File');
144 $info['body'] = _kt('New Help File'); 144 $info['body'] = _kt('New Help File');