Newer
Older
<?php
/*********************************************************************
class.faq.php
Backend support for article creates, edits, deletes, and attachments.
http://www.osticket.com
Released under the GNU General Public License WITHOUT ANY WARRANTY.
See LICENSE.TXT for details.
vim: expandtab sw=4 ts=4 sts=4:
**********************************************************************/
require_once('class.file.php');
require_once('class.category.php');
class FAQ {
var $id;
var $ht;
var $attachments;
function FAQ($id) {
$this->id=0;
$this->ht = array();
$this->load($id);
}
function load($id) {
$sql='SELECT faq.*,cat.ispublic, count(attach.file_id) as attachments '
.' FROM '.FAQ_TABLE.' faq '
.' LEFT JOIN '.FAQ_CATEGORY_TABLE.' cat ON(cat.category_id=faq.category_id) '
.' LEFT JOIN '.ATTACHMENT_TABLE.' attach
ON(attach.object_id=faq.faq_id AND attach.`type`=\'F\' AND attach.inline=0) '
.' WHERE faq.faq_id='.db_input($id)
if (!($res=db_query($sql)) || !db_num_rows($res))
return false;
$this->ht = db_fetch_array($res);
$this->ht['id'] = $this->id = $this->ht['faq_id'];
$this->category = null;
$this->attachments = new GenericAttachments($this->id, 'F');
return true;
}
function reload() {
return $this->load($this->getId());
}
/* ------------------> Getter methods <--------------------- */
function getId() { return $this->id; }
function getHashtable() { return $this->ht; }
function getKeywords() { return $this->ht['keywords']; }
function getQuestion() { return $this->ht['question']; }
function getAnswer() { return $this->ht['answer']; }
function getAnswerWithImages() {
return Format::viewableImages($this->ht['answer'], ROOT_PATH.'image.php');
function getSearchableAnswer() {
return ThreadBody::fromFormattedText($this->ht['answer'], 'html')
->getSearchable();
}
function getNotes() { return $this->ht['notes']; }
function getNumAttachments() { return $this->ht['attachments']; }
function isPublished() { return (!!$this->ht['ispublished'] && !!$this->ht['ispublic']); }
function getCreateDate() { return $this->ht['created']; }
function getUpdateDate() { return $this->ht['updated']; }
function getCategoryId() { return $this->ht['category_id']; }
if(!$this->category && $this->getCategoryId())
$this->category = Category::lookup($this->getCategoryId());
return $this->category;
}
function getHelpTopicsIds() {
if (!isset($this->ht['topics']) && ($topics=$this->getHelpTopics())) {
$this->ht['topics'] = array_keys($topics);
}
return $this->ht['topics'];
}
function getHelpTopics() {
//XXX: change it to obj (when needed)!
if (!isset($this->topics)) {
$this->topics = array();
$sql='SELECT t.topic_id, CONCAT_WS(" / ", pt.topic, t.topic) as name FROM '.TOPIC_TABLE.' t '
.' INNER JOIN '.FAQ_TOPIC_TABLE.' ft ON(ft.topic_id=t.topic_id AND ft.faq_id='.db_input($this->id).') '
.' LEFT JOIN '.TOPIC_TABLE.' pt ON(pt.topic_id=t.topic_pid) '
.' ORDER BY t.topic';
if (($res=db_query($sql)) && db_num_rows($res)) {
while(list($id,$name) = db_fetch_row($res))
$this->topics[$id]=$name;
}
}
return $this->topics;
}
/* ------------------> Setter methods <--------------------- */
function setPublished($val) { $this->ht['ispublished'] = !!$val; }
function setQuestion($question) { $this->ht['question'] = Format::striptags(trim($question)); }
function setAnswer($text) { $this->ht['answer'] = $text; }
function setKeywords($words) { $this->ht['keywords'] = $words; }
function setNotes($text) { $this->ht['notes'] = $text; }
/* For ->attach() and ->detach(), use $this->attachments() (nolint) */
function attach($file) { return $this->_attachments->add($file); }
function detach($file) { return $this->_attachments->remove($file); }
function publish() {
$this->setPublished(1);
return $this->apply();
}
function unpublish() {
$this->setPublished(0);
return $this->apply();
}
/* Same as update - but mainly called after one or more setters are changed. */
function apply() {
//XXX: set errors and add ->getErrors() & ->getError()
return $this->update($this->ht, $errors);
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// 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) {
$topics = $this->getHelpTopicsIds();
foreach($ids as $id) {
if($topics && in_array($id,$topics)) continue;
$sql='INSERT IGNORE INTO '.FAQ_TOPIC_TABLE
.' SET faq_id='.db_input($this->getId())
.', topic_id='.db_input($id);
db_query($sql);
}
}
$sql='DELETE FROM '.FAQ_TOPIC_TABLE.' WHERE faq_id='.db_input($this->getId());
if($ids)
$sql.=' AND topic_id NOT IN('.implode(',', db_input($ids)).')';
if (!$this->save($this->getId(), $vars, $errors))
return false;
$this->updateTopics($vars['topics']);
// General attachments (for all languages)
// ---------------------
// Delete removed attachments.
$keepers = $vars['files'];
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;
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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='';
if(($attachments=$this->attachments->getSeparates())) {
foreach($attachments as $attachment ) {
/* The h key must match validation in file.php */
$hash=$attachment['key'].md5($attachment['id'].session_id().strtolower($attachment['key']));
$size=sprintf(' <small>(<i>%s</i>)</small>',Format::file_size($attachment['size']));
$str.=sprintf('<a class="Icon file no-pjax" href="file.php?h=%s" target="%s">%s</a>%s %s',
$hash, $target, Format::htmlchars($attachment['name']), $size, $separator);
$sql='DELETE FROM '.FAQ_TABLE
.' WHERE faq_id='.db_input($this->getId())
.' LIMIT 1';
if(!db_query($sql) || !db_affected_rows())
return false;
//Cleanup help topics.
db_query('DELETE FROM '.FAQ_TOPIC_TABLE.' WHERE faq_id='.db_input($this->id));
//Cleanup attachments.
$this->attachments->deleteAll();
return true;
}
/* ------------------> Static methods <--------------------- */
if(!($id=self::create($vars, $errors)))
return false;
if(($faq=self::lookup($id))) {
if($_FILES['attachments'] && ($files=AttachmentFile::format($_FILES['attachments'])))
$faq->attachments->upload($files);
// Inline images (attached to the draft)
if (isset($vars['draft_id']) && $vars['draft_id'])
if ($draft = Draft::lookup($vars['draft_id']))
$faq->attachments->upload($draft->getAttachmentIds(), true);
$faq->reload();
}
return $faq;
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
return self::save(0, $vars, $errors);
}
function lookup($id) {
return ($id && is_numeric($id) && ($obj= new FAQ($id)) && $obj->getId()==$id)? $obj : null;
}
function countPublishedFAQs() {
$sql='SELECT count(faq.faq_id) '
.' FROM '.FAQ_TABLE.' faq '
.' INNER JOIN '.FAQ_CATEGORY_TABLE.' cat ON(cat.category_id=faq.category_id AND cat.ispublic=1) '
.' WHERE faq.ispublished=1';
return db_result(db_query($sql));
}
function findIdByQuestion($question) {
$sql='SELECT faq_id FROM '.FAQ_TABLE
.' WHERE question='.db_input($question);
list($id) =db_fetch_row(db_query($sql));
return $id;
}
function findByQuestion($question) {
return self::lookup($id);
return false;
}
function save($id, $vars, &$errors, $validation=false) {
//Cleanup.
$vars['question']=Format::striptags(trim($vars['question']));
//validate
if($id && $id!=$vars['id'])
$errors['err'] = __('Internal error. Try again');
$errors['question'] = __('Question required');
elseif(($qid=self::findIdByQuestion($vars['question'])) && $qid!=$id)
$errors['question'] = __('Question already exists');
if(!$vars['category_id'] || !($category=Category::lookup($vars['category_id'])))
$errors['category_id'] = __('Category is required');
$errors['answer'] = __('FAQ answer is required');
if($errors || $validation) return (!$errors);
//save
$sql=' updated=NOW() '
.', question='.db_input($vars['question'])
.', answer='.db_input(Format::sanitize($vars['answer'], false))
.', category_id='.db_input($vars['category_id'])
.', ispublished='.db_input(isset($vars['ispublished'])?$vars['ispublished']:0)
.', notes='.db_input(Format::sanitize($vars['notes']));
if($id) {
$sql='UPDATE '.FAQ_TABLE.' SET '.$sql.' WHERE faq_id='.db_input($id);
if(db_query($sql))
return true;
$errors['err']=sprintf(__('Unable to update %s.'), __('this FAQ article'));
} else {
$sql='INSERT INTO '.FAQ_TABLE.' SET '.$sql.',created=NOW()';
if (db_query($sql) && ($id=db_insert_id())) {
Signal::send('model.created', FAQ::lookup($id));
$errors['err']=sprintf(__('Unable to create %s.'), __('this FAQ article'))
.' '.__('Internal error occurred');