diff --git a/include/class.attachment.php b/include/class.attachment.php index d562b31a095fa69251f62af687d14f40dfda0243..1a34d79d79e6f73804a5ef861ab49007eef70073 100644 --- a/include/class.attachment.php +++ b/include/class.attachment.php @@ -116,7 +116,7 @@ class GenericAttachments { function getId() { return $this->id; } function getType() { return $this->type; } - function upload($files, $inline=false) { + function upload($files, $inline=false, $lang=false) { $i=array(); if (!is_array($files)) $files=array($files); foreach ($files as $file) { @@ -134,6 +134,9 @@ class GenericAttachments { .',object_id='.db_input($this->getId()) .',file_id='.db_input($fileId) .',inline='.db_input($_inline ? 1 : 0); + if ($lang) + $sql .= ',lang='.db_input($lang); + // File may already be associated with the draft (in the // event it was deleted and re-added) if (db_query($sql, function($errno) { return $errno != 1062; }) @@ -164,14 +167,14 @@ class GenericAttachments { return $fileId; } - function getInlines() { return $this->_getList(false, true); } - function getSeparates() { return $this->_getList(true, false); } - function getAll() { return $this->_getList(true, true); } + function getInlines($lang=false) { return $this->_getList(false, true, $lang); } + function getSeparates($lang=false) { return $this->_getList(true, false, $lang); } + function getAll($lang=false) { return $this->_getList(true, true, $lang); } - function _getList($separate=false, $inlines=false) { + function _getList($separate=false, $inlines=false, $lang=false) { if(!isset($this->attachments)) { $this->attachments = array(); - $sql='SELECT f.id, f.size, f.`key`, f.name, a.inline ' + $sql='SELECT f.id, f.size, f.`key`, f.name, a.inline, a.lang ' .' FROM '.FILE_TABLE.' f ' .' INNER JOIN '.ATTACHMENT_TABLE.' a ON(f.id=a.file_id) ' .' WHERE a.`type`='.db_input($this->getType()) @@ -186,7 +189,8 @@ class GenericAttachments { } $attachments = array(); foreach ($this->attachments as $a) { - if ($a['inline'] != $separate || $a['inline'] == $inlines) { + if (($a['inline'] != $separate || $a['inline'] == $inlines) + && $lang == $a['lang']) { $a['file_id'] = $a['id']; $a['hash'] = md5($a['file_id'].session_id().strtolower($a['key'])); $attachments[] = $a; diff --git a/include/class.faq.php b/include/class.faq.php index 0395b4968671f9a487167443d66ef242d7f9da04..6ba85483048d105072dc1c9621cada147aad0f12 100644 --- a/include/class.faq.php +++ b/include/class.faq.php @@ -140,6 +140,44 @@ class FAQ { return $this->update($this->ht, $errors); } + // Internationalization of the knowledge base + + function getTranslateTag($subtag) { + return _H(sprintf('faq.%s.%s', $subtag, $this->id)); + } + function getLocal($subtag) { + $tag = $this->getTranslateTag($subtag); + $T = CustomDataTranslation::translate($tag); + return $T != $tag ? $T : $this->ht[$subtag]; + } + function getAllTranslations() { + if (!isset($this->_local)) { + $tag = $this->getTranslateTag('q:a'); + $this->_local = CustomDataTranslation::allTranslations($tag, 'article'); + } + return $this->_local; + } + function getLocalQuestion($lang=false) { + return $this->_getLocal('q', $lang); + } + function getLocalBodyWithImages($lang=false) { + return $this->_getLocal('a', $lang); + } + function _getLocal($what, $lang=false) { + if (!$lang) { + $lang = Internationalization::getCurrentLanguage(); + } + $translations = $this->getAllTranslations(); + foreach ($translations as $t) { + if ($lang == $t->lang) { + $data = $t->getComplex(); + if (isset($data[$what])) + return $data[$what]; + } + } + return $this->ht[$what]; + } + function updateTopics($ids){ if($ids) { @@ -164,34 +202,104 @@ class FAQ { } function update($vars, &$errors) { + global $cfg; - if(!$this->save($this->getId(), $vars, $errors)) + if (!$this->save($this->getId(), $vars, $errors)) return false; $this->updateTopics($vars['topics']); - //Delete removed attachments. + // General attachments (for all languages) + // --------------------- + // Delete removed attachments. $keepers = $vars['files']; - if(($attachments = $this->attachments->getSeparates())) { + if (($attachments = $this->attachments->getSeparates())) { foreach($attachments as $file) { if($file['id'] && !in_array($file['id'], $keepers)) $this->attachments->delete($file['id']); } } - // Upload new attachments IF any. $this->attachments->upload($keepers); + // Handle language-specific attachments + // ---------------------- + $langs = $cfg ? $cfg->getSecondaryLanguages() : false; + if ($langs) { + $langs[] = $cfg->getPrimaryLanguage(); + foreach ($langs as $lang) { + $keepers = $vars['files_'.$lang]; + + // Delete removed attachments. + if (($attachments = $this->attachments->getSeparates($lang))) { + foreach ($attachments as $file) { + if ($file['id'] && !in_array($file['id'], $keepers)) + $this->attachments->delete($file['id']); + } + } + // Upload new attachments IF any. + $this->attachments->upload($keepers, false, $lang); + } + } + // Inline images (attached to the draft) $this->attachments->deleteInlines(); $this->attachments->upload(Draft::getAttachmentIds($vars['answer'])); + if (!$this->saveTranslations($vars)) + return false; + $this->reload(); Signal::send('model.updated', $this); return true; } + function saveTranslations($vars) { + global $thisstaff; + + foreach ($this->getAllTranslations() as $t) { + $trans = @$vars['trans'][$t->lang]; + if (!array_filter($trans)); + continue; + + // Content is not new and shouldn't be added below + unset($vars['trans'][$t->lang]); + $content = array('q' => $trans['question'], + 'a' => Format::sanitize($trans['answer'])); + + // Don't update content which wasn't updated + if ($content == $t->getComplex()) + continue; + + $t->text = $content; + $t->agent_id = $thisstaff->getId(); + $t->updated = SqlFunction::NOW(); + if (!$t->save()) + return false; + } + // New translations (?) + $tag = $this->getTranslateTag('q:a'); + foreach ($vars['trans'] as $lang=>$parts) { + $content = array('q' => @$parts['question'], + 'a' => Format::sanitize(@$parts['answer'])); + if (!array_filter($content)) + continue; + $t = CustomDataTranslation::create(array( + 'type' => 'article', + 'object_hash' => $tag, + 'lang' => $lang, + 'text' => $content, + 'revision' => 1, + 'agent_id' => $thisstaff->getId(), + 'updated' => SqlFunction::NOW(), + )); + if (!$t->save()) + return false; + } + return true; + } + function getAttachmentsLinks($separator=' ',$target='') { $str=''; diff --git a/include/staff/faq.inc.php b/include/staff/faq.inc.php index ee27bfab24df1d25ba9804b5c28be058cbc9cae8..ce5959f2ce271f74af9259a257950f03bd6d7d06 100644 --- a/include/staff/faq.inc.php +++ b/include/staff/faq.inc.php @@ -12,6 +12,20 @@ if($faq){ $info['answer']=Format::viewableImages($faq->getAnswer()); $info['notes']=Format::viewableImages($faq->getNotes()); $qstr='id='.$faq->getId(); + $langs = $cfg->getSecondaryLanguages(); + $translations = $faq->getAllTranslations(); + foreach ($cfg->getSecondaryLanguages() as $tag) { + foreach ($translations as $t) { + if (strcasecmp($t->lang, $tag) === 0) { + $trans = $t->getComplex(); + $info['trans'][$tag] = array( + 'question' => $trans['q'], + 'answer' => Format::viewableImages($trans['a']), + ); + break; + } + } + } }else { $title=__('Add New FAQ'); $action='create'; @@ -30,122 +44,194 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>"> <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> <h2><?php echo __('FAQ');?></h2> - <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2"> - <thead> - <tr><td></td><td></td></tr> <!-- For fixed table layout --> - <tr> - <th colspan="2"> - <h4><?php echo $title; ?></h4> - </th> - </tr> - </thead> - <tbody> - <tr> - <th colspan="2"> - <em><?php echo __('FAQ Information');?></em> - </th> - </tr> - <tr> - <td colspan=2> - <div style="padding-top:3px;"><b><?php echo __('Question');?></b> <span class="error">* <?php echo $errors['question']; ?></span></div> - <input type="text" size="70" name="question" value="<?php echo $info['question']; ?>"> - </td> - </tr> - <tr> - <td colspan=2> - <div><b><?php echo __('Category Listing');?></b>: <span class="faded"><?php echo __('FAQ category the question belongs to.');?></span></div> - <select name="category_id" style="width:350px;"> - <option value="0"><?php echo __('Select FAQ Category');?> </option> - <?php - $sql='SELECT category_id, name, ispublic FROM '.FAQ_CATEGORY_TABLE; - if(($res=db_query($sql)) && db_num_rows($res)) { - while($row=db_fetch_array($res)) { - echo sprintf('<option value="%d" %s>%s (%s)</option>', - $row['category_id'], - (($info['category_id']==$row['category_id'])?'selected="selected"':''), - $row['name'], - ($info['ispublic']?__('Public'):__('Internal'))); - } - } - ?> - </select> - <span class="error">* <?php echo $errors['category_id']; ?></span> - </td> - </tr> - <tr> - <td colspan=2> - <div><b><?php echo __('Listing Type');?></b>: - <i class="help-tip icon-question-sign" href="#listing_type"></i></div> - <input type="radio" name="ispublished" value="1" <?php echo $info['ispublished']?'checked="checked"':''; ?>><?php echo __('Public (publish)');?> - - <input type="radio" name="ispublished" value="0" <?php echo !$info['ispublished']?'checked="checked"':''; ?>><?php echo __('Internal (private)');?> - <span class="error">* <?php echo $errors['ispublished']; ?></span> - </td> - </tr> - <tr> - <td colspan=2> - <div style="margin-bottom:0.5em;margin-top:0.5em"> - <b><?php echo __('Answer');?></b> <font class="error">* <?php echo $errors['answer']; ?></font></div> - </div> - <textarea name="answer" cols="21" rows="12" - style="width:98%;" class="richtext draft" <?php - list($draft, $attrs) = Draft::getDraftAndDataAttrs('faq', - is_object($faq) ? $faq->getId() : false, $info['answer']); - echo $attrs; ?>><?php echo $draft ?: $info['answer']; - ?></textarea> - </td> - </tr> - <tr> - <td colspan=2> - <div><h3><?php echo __('Attachments');?> - <span class="faded">(<?php echo __('optional');?>)</span></h3> - <div class="error"><?php echo $errors['files']; ?></div> - </div> - <?php - $attachments = $faq_form->getField('attachments'); - if ($faq && ($files=$faq->attachments->getSeparates())) { - $ids = array(); - foreach ($files as $f) - $ids[] = $f['id']; - $attachments->value = $ids; - } - print $attachments->render(); ?> - <br/> - </td> - </tr> +<?php if ($info['question']) { ?> + <h4><?php echo $info['question']; ?></h4> +<?php } ?> + <div> + <div> + <b><?php echo __('Category Listing');?></b>: + <span class="error">*</span> + <span class="faded"><?php echo __('FAQ category the question belongs to.');?></span> + </div> + <select name="category_id" style="width:350px;"> + <option value="0"><?php echo __('Select FAQ Category');?> </option> <?php - if ($topics = Topic::getAllHelpTopics()) { ?> - <tr> - <th colspan="2"> - <em><strong><?php echo __('Help Topics');?></strong>: <?php echo __('Check all help topics related to this FAQ.');?></em> - </th> - </tr> - <tr><td colspan="2"> - <?php - while (list($topicId,$topic) = each($topics)) { - echo sprintf('<input type="checkbox" name="topics[]" value="%d" %s>%s<br>', - $topicId, - (($info['topics'] && in_array($topicId,$info['topics']))?'checked="checked"':''), - $topic); + $sql='SELECT category_id, name, ispublic FROM '.FAQ_CATEGORY_TABLE; + if(($res=db_query($sql)) && db_num_rows($res)) { + while($row=db_fetch_array($res)) { + echo sprintf('<option value="%d" %s>%s (%s)</option>', + $row['category_id'], + (($info['category_id']==$row['category_id'])?'selected="selected"':''), + $row['name'], + ($info['ispublic']?__('Public'):__('Internal'))); } - ?> - </td> - </tr> - <?php - } ?> - <tr> - <th colspan="2"> - <em><strong><?php echo __('Internal Notes');?></strong>: </em> - </th> - </tr> - <tr> - <td colspan=2> - <textarea class="richtext no-bar" name="notes" cols="21" - rows="8" style="width: 80%;"><?php echo $info['notes']; ?></textarea> - </td> - </tr> - </tbody> -</table> + } + ?> + </select> + <div class="error"><?php echo $errors['category_id']; ?></div> + +<?php +if ($topics = Topic::getAllHelpTopics()) { ?> + <div style="padding-top:9px"> + <strong><?php echo __('Help Topics');?></strong>: + <?php echo __('Check all help topics related to this FAQ.');?> + </div> + <select multiple="multiple" name="topics[]" class="multiselect" + id="help-topic-selection" style="width:350px;"> + <?php while (list($topicId,$topic) = each($topics)) { ?> + <option value="<?php echo $topicId; ?>" <?php + if (in_array($topicID, $info['topics'])) echo 'selected="selected"'; + ?>><?php echo $topic; ?></option> + <?php } ?> + </select> + <script type="text/javascript"> + $(function() { $("#help-topic-selection").multiselect({ + noneSelectedText: '<?php echo __('Help Topics'); ?>'}); + }); + </script> +<?php } ?> + + <div style="padding-top:9px;"> + <b><?php echo __('Listing Type');?></b>: + <span class="error">*</span> + <i class="help-tip icon-question-sign" href="#listing_type"></i> + </div> + <select name="ispublished"> + <option value="1" <?php echo $info['ispublished'] ? 'selected="selected"' : ''; ?>> + <?php echo __('Public (publish)'); ?> + </option> + <option value="0" <?php echo !$info['ispublished'] ? 'selected="selected"' : ''; ?>> + <?php echo __('Internal (private)'); ?> + </option> + </select> + <div class="error"><?php echo $errors['ispublished']; ?></div> +</div> + +<ul class="tabs" style="margin-top:9px;"> + <li class="active"><a class="active" href="#article"><?php echo __('Article Content'); ?></a></li> + <li><a href="#attachments"><?php echo __('Attachments') . sprintf(' (%d)', + count($faq->attachments->getSeparates(''))); ?></a></li> + <li><a href="#notes"><?php echo __('Internal Notes'); ?></a></li> +</ul> + +<div class="tab_content" id="article"> +<?php if ($faq && ($langs = $cfg->getSecondaryLanguages())) { ?> + <div class="banner" style="margin-top:12px;"> + <span class="pull-left" style="padding-top:2px"> + <i class="icon-globe icon-large"></i> + <?php echo __('This content is translatable'); ?> + </span> + <span class="pull-right"> + <?php echo __('View'); ?>: + <select onchange="javascript: +$('option', this).each(function(){$($(this).val()).hide()}); +$($('option:selected', this).val()).show(); "> + <option value="#reference-text"><?php echo + Internationalization::getLanguageDescription($cfg->getPrimaryLanguage()); + ?> — <?php echo __('Primary'); ?></option> +<?php foreach ($langs as $tag) { ?> + <option value="#translation-<?php echo $tag; ?>"><?php echo + Internationalization::getLanguageDescription($tag); + ?></option> +<?php } ?> + </select> + </span> + </div> +<?php } ?> + + <div id="reference-text"> + <div style="padding-top:9px;"> + <b><?php echo __('Question');?> + <span class="error">*</span> + </b> + <div class="error"><?php echo $errors['question']; ?></div> + </div> + <input type="text" size="70" name="question" + style="font-size:105%;display:block;width:98%" + value="<?php echo $info['question']; ?>"> + <div style="margin-bottom:0.5em;margin-top:9px"> + <b><?php echo __('Answer');?></b> + <span class="error">*</span> + <div class="error"><?php echo $errors['answer']; ?></div> + </div> + <textarea name="answer" cols="21" rows="12" + style="width:98%;" class="richtext draft" <?php +list($draft, $attrs) = Draft::getDraftAndDataAttrs('faq', +is_object($faq) ? $faq->getId() : false, $info['answer']); +echo $attrs; ?>><?php echo $draft ?: $info['answer']; + ?></textarea> + +<?php if (count($langs)) { + $lang = $cfg->getPrimaryLanguage(); ?> + <div style="padding-top:9px"> + <strong><?php echo sprintf(__( + /* %s is the name of a language */ 'Attachments for %s'), + Internationalization::getLanguageDescription($lang)); + ?></strong> + <div style="margin:0 0 3px"><em class="faded"><?php echo __( + 'These attachments are only available when article is rendered in this language.' + ); ?></em></div> + </div> + <?php + print $faq_form->getField('attachments.'.$lang)->render(); ?> +<?php } ?> + </div> <!-- end of reference-text --> + +<?php if ($langs && $faq) { + foreach ($langs as $tag) { ?> + <div id="translation-<?php echo $tag; ?>" style="display:none" lang="<?php echo $tag; ?>"> + <div style="padding-top:9px;"> + <b><?php echo __('Question');?> + <span class="error">*</span> + </b> + <div class="error"><?php echo $errors['question']; ?></div> + </div> + <input type="text" size="70" name="trans[<?php echo $tag; ?>][question]" + style="font-size:105%;display:block;width:98%" + value="<?php echo $info['trans'][$tag]['question']; ?>"> + <div style="margin-bottom:0.5em;margin-top:9px"> + <b><?php echo __('Answer');?></b> + <span class="error">*</span> + <div class="error"><?php echo $errors['answer']; ?></div> + </div> + <textarea name="trans[<?php echo $tag; ?>][answer]" cols="21" rows="12" + style="width:98%;" class="richtext draft" <?php +list($draft, $attrs) = Draft::getDraftAndDataAttrs('faq', $faq->getId().'.'.$tag, + $info['trans'][$tag]['answer']); +echo $attrs; ?>><?php echo $draft ?: $info['trans'][$tag]['answer']; + ?></textarea> + + <div style="padding-top:9px"> + <strong><?php echo sprintf(__('Attachments for %s'), + Internationalization::getLanguageDescription($tag)); + ?></strong> + <div style="margin:0 0 3px"><em class="faded"><?php echo __( + 'These attachments are only available when article is rendered in this language.' + ); ?></em></div> + </div> + <?php + print $faq_form->getField('attachments.'.$tag)->render(); ?> + </div> <!-- End of this language --> +<?php } ?> +<?php } ?> +</div> + +<div class="tab_content" id="attachments" style="display:none"> + <div> + <p><em><?php echo __( + 'These attachments are always available, regardless of the language in which the article is rendered' + ); ?></em></p> + <div class="error"><?php echo $errors['files']; ?></div> + </div> + <?php + print $faq_form->getField('attachments')->render(); ?> +</div> + +<div class="tab_content" style="display:none;" id="notes"> + <textarea class="richtext no-bar" name="notes" cols="21" + rows="8" style="width: 80%;"><?php echo $info['notes']; ?></textarea> +</div> + <p style="text-align:center;"> <input type="submit" name="submit" value="<?php echo $submit_text; ?>"> <input type="reset" name="reset" value="<?php echo __('Reset'); ?>" onclick="javascript: @@ -155,3 +241,5 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>" onclick='window.location.href="faq.php?<?php echo $qstr; ?>"'> </p> </form> + +<link rel="stylesheet" type="text/css" href="<?php echo ROOT_PATH; ?>css/jquery.multiselect.css" /> diff --git a/scp/faq.php b/scp/faq.php index 2a3c2e95116e2b6da074df0e0655366d581ddec3..868cebbae341973d6b8b15072b4c94744f07d24e 100644 --- a/scp/faq.php +++ b/scp/faq.php @@ -23,16 +23,42 @@ if($_REQUEST['id'] && !($faq=FAQ::lookup($_REQUEST['id']))) if($_REQUEST['cid'] && !$faq && !($category=Category::lookup($_REQUEST['cid']))) $errors['err']=sprintf(__('%s: Unknown or invalid'), __('FAQ category')); -$faq_form = new Form(array( +$form_fields = array( + // Attachments for all languages — that is, attachments not specific to + // a particular language 'attachments' => new FileUploadField(array('id'=>'attach', 'configuration'=>array('extensions'=>false, 'size'=>$cfg->getMaxFileSize()) - )), -)); + )), +); -if($_POST): +// Build attachment lists for language-specific attachment fields +if ($langs = $cfg->getSecondaryLanguages()) { + // Primary-language specific files + $langs[] = $cfg->getPrimaryLanguage(); + // Secondary-language specific files + foreach ($langs as $l) { + $form_fields['attachments.'.$l] = new FileUploadField(array( + 'id'=>'attach','name'=>'attach:'.$l, + 'configuration'=>array('extensions'=>false, + 'size'=>$cfg->getMaxFileSize()) + )); + } +} + +$faq_form = new Form($form_fields, $_POST); + +if ($_POST) { $errors=array(); + // General attachments $_POST['files'] = $faq_form->getField('attachments')->getClean(); + // Language-specific attachments + if ($langs) { + $langs[] = $cfg->getPrimaryLanguage(); + foreach ($langs as $lang) { + $_POST['files_'.$lang] = $faq_form->getField('attachments.'.$lang)->getClean(); + } + } switch(strtolower($_POST['do'])) { case 'create': case 'add': @@ -97,8 +123,32 @@ if($_POST): $errors['err']=__('Unknown action'); } -endif; - +} +else { + // Not a POST — load database-backed attachments to attachment fields + if ($langs && $faq) { + // Multi-lingual system + foreach ($langs as $lang) { + $attachments = $faq_form->getField('attachments.'.$lang); + if ($files = $faq->attachments->getSeparates($lang)) { + $ids = array(); + foreach ($files as $f) + $ids[] = $f['id']; + $attachments->value = $ids; + } + } + } + if ($faq) { + // Common attachments + $attachments = $faq_form->getField('attachments'); + if ($files = $faq->attachments->getSeparates()) { + $ids = array(); + foreach ($files as $f) + $ids[] = $f['id']; + $attachments->value = $ids; + } + } +} $inc='faq-categories.inc.php'; //FAQs landing page. if($faq) {