Completed
Branch master (1b2f30)
by Michael
06:29 queued 03:22
created

sfAnswer::incrementDownload()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
/**
4
 * Module: SmartFAQ
5
 * Author: The SmartFactory <www.smartfactory.ca>
6
 * Licence: GNU
7
 */
8
9
// defined('XOOPS_ROOT_PATH') || exit('XOOPS root path not defined');
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
10
11
// Answers status
12
define('_SF_AN_STATUS_NOTSET', -1);
13
define('_SF_AN_STATUS_PROPOSED', 0);
14
define('_SF_AN_STATUS_APPROVED', 1);
15
define('_SF_AN_STATUS_REJECTED', 2);
16
17
// Notification Events
18
define('_SF_NOT_ANSWER_APPROVED', 3);
19
define('_SF_NOT_ANSWER_REJECTED', 4);
20
21
/**
22
 * Class sfAnswer
23
 */
24
class sfAnswer extends XoopsObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
25
{
26
    public $attachment_array = array();
27
28
    /**
29
     * constructor
30
     * @param null $id
31
     */
32
    public function __construct($id = null)
33
    {
34
        $this->db = XoopsDatabaseFactory::getDatabaseConnection();
35
        $this->initVar('answerid', XOBJ_DTYPE_INT, null, false);
36
        $this->initVar('status', XOBJ_DTYPE_INT, -1, false);
37
        $this->initVar('faqid', XOBJ_DTYPE_INT, null, false);
38
        $this->initVar('answer', XOBJ_DTYPE_TXTAREA, null, true);
39
        $this->initVar('uid', XOBJ_DTYPE_INT, 0, false);
40
        $this->initVar('datesub', XOBJ_DTYPE_INT, null, false);
41
        $this->initVar('notifypub', XOBJ_DTYPE_INT, 0, false);
42
43
        $this->initVar('attachment', XOBJ_DTYPE_TXTAREA, '');
44
45
        $this->initVar('dohtml', XOBJ_DTYPE_INT, 1, false);
46
        $this->initVar('doxcode', XOBJ_DTYPE_INT, 1, false);
47
        $this->initVar('dosmiley', XOBJ_DTYPE_INT, 1, false);
48
        $this->initVar('doimage', XOBJ_DTYPE_INT, 0, false);
49
        $this->initVar('dobr', XOBJ_DTYPE_INT, 1, false);
50
51
        // for backward compatibility
52
        if (isset($id)) {
53
            if (is_array($id)) {
54
                $this->assignVars($id);
55
            } else {
56
                $answerHandler = new sfAnswerHandler($this->db);
57
                $answer        = $answerHandler->get($id);
58
                foreach ($answer->vars as $k => $v) {
59
                    $this->assignVar($k, $v['value']);
60
                }
61
            }
62
        }
63
    }
64
65
    // ////////////////////////////////////////////////////////////////////////////////////
66
    // attachment functions    TODO: there should be a file/attachment management class
67
    /**
68
     * @return array|mixed|null
69
     */
70
    public function getAttachment()
71
    {
72
        if (count($this->attachment_array)) {
73
            return $this->attachment_array;
74
        }
75
        $attachment = $this->getVar('attachment');
76
        if (empty($attachment)) {
77
            $this->attachment_array = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $attachment_array.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
78
        } else {
79
            $this->attachment_array = @unserialize(base64_decode($attachment));
80
        }
81
82
        return $this->attachment_array;
83
    }
84
85
    /**
86
     * @param $attach_key
87
     * @return bool
88
     */
89
    public function incrementDownload($attach_key)
90
    {
91
        if (!$attach_key) {
92
            return false;
93
        }
94
        $this->attachment_array[(string)$attach_key]['num_download']++;
95
96
        return $this->attachment_array[(string)$attach_key]['num_download'];
97
    }
98
99
    /**
100
     * @return bool
101
     */
102
    public function saveAttachment()
0 ignored issues
show
Coding Style introduced by
saveAttachment uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
103
    {
104
        $attachment_save = '';
105 View Code Duplication
        if (is_array($this->attachment_array) && count($this->attachment_array) > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
            $attachment_save = base64_encode(serialize($this->attachment_array));
107
        }
108
        $this->setVar('attachment', $attachment_save);
109
        $sql = 'UPDATE ' . $GLOBALS['xoopsDB']->prefix('smartfaq_answers') . ' SET attachment=' . $GLOBALS['xoopsDB']->quoteString($attachment_save) . ' WHERE post_id = ' . $this->getVar('answerid');
110
        if (!$result = $GLOBALS['xoopsDB']->queryF($sql)) {
111
            //xoops_error($GLOBALS["xoopsDB"]->error());
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
112
            return false;
113
        }
114
115
        return true;
116
    }
117
118
    /**
119
     * @param  null $attach_array
120
     * @return bool
121
     */
122
    public function deleteAttachment($attach_array = null)
123
    {
124
        global $xoopsModuleConfig;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
125
126
        $attach_old = $this->getAttachment();
127
        if (!is_array($attach_old) || count($attach_old) < 1) {
128
            return true;
129
        }
130
        $this->attachment_array = array();
131
132
        if ($attach_array === null) {
133
            $attach_array = array_keys($attach_old);
134
        } // to delete all!
135
        if (!is_array($attach_array)) {
136
            $attach_array = array($attach_array);
137
        }
138
139
        foreach ($attach_old as $key => $attach) {
140
            if (in_array($key, $attach_array)) {
141
                @unlink(XOOPS_ROOT_PATH . '/' . $xoopsModuleConfig['dir_attachments'] . '/' . $attach['name_saved']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
142
                @unlink(XOOPS_ROOT_PATH . '/' . $xoopsModuleConfig['dir_attachments'] . '/thumbs/' . $attach['name_saved']); // delete thumbnails
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
143
                continue;
144
            }
145
            $this->attachment_array[$key] = $attach;
146
        }
147
        $attachment_save = '';
148 View Code Duplication
        if (is_array($this->attachment_array) && count($this->attachment_array) > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
149
            $attachment_save = base64_encode(serialize($this->attachment_array));
150
        }
151
        $this->setVar('attachment', $attachment_save);
152
153
        return true;
154
    }
155
156
    /**
157
     * @param  string $name_saved
158
     * @param  string $name_display
159
     * @param  string $mimetype
160
     * @param  int    $num_download
161
     * @return bool
162
     */
163
    public function setAttachment($name_saved = '', $name_display = '', $mimetype = '', $num_download = 0)
164
    {
165
        static $counter = 0;
166
        $this->attachment_array = $this->getAttachment();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getAttachment() of type * is incompatible with the declared type array of property $attachment_array.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
167
        if ($name_saved) {
168
            $key                          = (string)(time() + ($counter++));
169
            $this->attachment_array[$key] = array(
170
                'name_saved'   => $name_saved,
171
                'name_display' => isset($name_display) ? $name_display : $name_saved,
172
                'mimetype'     => $mimetype,
173
                'num_download' => isset($num_download) ? (int)$num_download : 0
174
            );
175
        }
176
        $attachment_save = null;
177
        if (is_array($this->attachment_array)) {
178
            $attachment_save = base64_encode(serialize($this->attachment_array));
179
        }
180
        $this->setVar('attachment', $attachment_save);
181
182
        return true;
183
    }
184
185
    /**
186
     * TODO: refactor
187
     * @param  bool $asSource
188
     * @return string
189
     */
190
    public function displayAttachment($asSource = false)
191
    {
192
        global $xoopsModule, $xoopsModuleConfig;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
193
194
        $post_attachment = '';
195
        $attachments     = $this->getAttachment();
196
        if (is_array($attachments) && count($attachments) > 0) {
197
            $iconHandler = sf_getIconHandler();
198
            $mime_path   = $iconHandler->getPath('mime');
199
            include_once XOOPS_ROOT_PATH . '/modules/' . $xoopsModule->getVar('dirname', 'n') . '/include/functions.image.php';
200
            $image_extensions = array('jpg', 'jpeg', 'gif', 'png', 'bmp'); // need improve !!!
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
201
            $post_attachment .= '<br /><strong>' . _MD_ATTACHMENT . '</strong>:';
202
            $post_attachment .= '<br /><hr size="1" noshade="noshade" /><br />';
203
            foreach ($attachments as $key => $att) {
204
                $file_extension = ltrim(strrchr($att['name_saved'], '.'), '.');
205
                $filetype       = $file_extension;
206
                if (file_exists(XOOPS_ROOT_PATH . '/' . $mime_path . '/' . $filetype . '.gif')) {
207
                    $icon_filetype = XOOPS_URL . '/' . $mime_path . '/' . $filetype . '.gif';
208
                } else {
209
                    $icon_filetype = XOOPS_URL . '/' . $mime_path . '/unknown.gif';
210
                }
211
                $file_size = @filesize(XOOPS_ROOT_PATH . '/' . $xoopsModuleConfig['dir_attachments'] . '/' . $att['name_saved']);
212
                $file_size = number_format($file_size / 1024, 2) . ' KB';
213
                if ($xoopsModuleConfig['media_allowed'] && in_array(strtolower($file_extension), $image_extensions)) {
214
                    $post_attachment .= '<br /><img src="' . $icon_filetype . '" alt="' . $filetype . '" /><strong>&nbsp; ' . $att['name_display'] . '</strong> <small>(' . $file_size . ')</small>';
215
                    $post_attachment .= '<br />' . sf_attachmentImage($att['name_saved']);
216
                    $isDisplayed = true;
0 ignored issues
show
Unused Code introduced by
$isDisplayed is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
217
                } else {
218
                    global $xoopsUser;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
219
                    if (empty($xoopsModuleConfig['show_userattach'])) {
220
                        $post_attachment .= '<a href="' . XOOPS_URL . '/modules/' . $xoopsModule->getVar('dirname', 'n') . '/dl_attachment.php?attachid=' . $key . '&amp;post_id=' . $this->getVar('post_id') . '"> <img src="' . $icon_filetype . '" alt="' . $filetype . '" /> ' . $att['name_display'] . '</a> ' . _MD_FILESIZE . ': ' . $file_size . '; ' . _MD_HITS . ': ' . $att['num_download'];
221
                    } elseif ($xoopsUser && $xoopsUser->uid() > 0 && $xoopsUser->isactive()) {
222
                        $post_attachment .= '<a href="' . XOOPS_URL . '/modules/' . $xoopsModule->getVar('dirname', 'n') . '/dl_attachment.php?attachid=' . $key . '&amp;post_id=' . $this->getVar('post_id') . '"> <img src="' . $icon_filetype . '" alt="' . $filetype . '" /> ' . $att['name_display'] . '</a> ' . _MD_FILESIZE . ': ' . $file_size . '; ' . _MD_HITS . ': ' . $att['num_download'];
223
                    } else {
224
                        $post_attachment .= _MD_NEWBB_SEENOTGUEST;
225
                    }
226
                }
227
                $post_attachment .= '<br />';
228
            }
229
        }
230
231
        return $post_attachment;
232
    }
233
    // attachment functions
234
    // ////////////////////////////////////////////////////////////////////////////////////
235
236
    /**
237
     * @param  bool $force
238
     * @return bool
239
     */
240
    public function store($force = true)
241
    {
242
        $answerHandler = new sfAnswerHandler($this->db);
243
244
        if ($this->status() == _SF_AN_STATUS_APPROVED) {
245
            $criteria = new CriteriaCompo(new criteria('faqid', $this->faqid()));
246
            $answerHandler->updateAll('status', _SF_AN_STATUS_REJECTED, $criteria);
247
        }
248
249
        return $answerHandler->insert($this, $force);
250
    }
251
252
    /**
253
     * @return mixed
254
     */
255
    public function answerid()
256
    {
257
        return $this->getVar('answerid');
258
    }
259
260
    /**
261
     * @return mixed
262
     */
263
    public function faqid()
264
    {
265
        return $this->getVar('faqid');
266
    }
267
268
    /**
269
     * @param  string $format
270
     * @return mixed
271
     */
272
    public function answer($format = 'S')
273
    {
274
        return $this->getVar('answer', $format);
275
    }
276
277
    /**
278
     * @return mixed
279
     */
280
    public function uid()
281
    {
282
        return $this->getVar('uid');
283
    }
284
285
    /**
286
     * @param  string $dateFormat
287
     * @param  string $format
288
     * @return string
289
     */
290 View Code Duplication
    public function datesub($dateFormat = 'none', $format = 'S')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
    {
292
        if ($dateFormat === 'none') {
293
            $smartModuleConfig = sf_getModuleConfig();
294
            $dateFormat        = $smartModuleConfig['dateformat'];
295
        }
296
297
        return formatTimestamp($this->getVar('datesub', $format), $dateFormat);
298
    }
299
300
    /**
301
     * @return mixed
302
     */
303
    public function status()
304
    {
305
        return $this->getVar('status');
306
    }
307
308
    /**
309
     * @return bool
310
     */
311
    public function notLoaded()
312
    {
313
        return ($this->getVar('answerid') == -1);
314
    }
315
316
    /**
317
     * @param array $notifications
318
     */
319
    public function sendNotifications($notifications = array())
320
    {
321
        $smartModule = sf_getModuleInfo();
322
323
        $myts                = MyTextSanitizer::getInstance();
324
        $notificationHandler = xoops_getHandler('notification');
325
326
        $faqObj = new sfFaq($this->faqid());
327
328
        $tags                  = array();
329
        $tags['MODULE_NAME']   = $myts->displayTarea($smartModule->getVar('name'));
330
        $tags['FAQ_NAME']      = $faqObj->question();
331
        $tags['FAQ_URL']       = XOOPS_URL . '/modules/' . $smartModule->getVar('dirname') . '/faq.php?faqid=' . $faqObj->faqid();
332
        $tags['CATEGORY_NAME'] = $faqObj->getCategoryName();
333
        $tags['CATEGORY_URL']  = XOOPS_URL . '/modules/' . $smartModule->getVar('dirname') . '/category.php?categoryid=' . $faqObj->categoryid();
334
        $tags['FAQ_QUESTION']  = $faqObj->question();
335
336
        // TODO : Not sure about the 'formpreview' ...
337
        $tags['FAQ_ANSWER'] = $this->answer('formpreview');
338
        $tags['DATESUB']    = $this->datesub();
339
340
        foreach ($notifications as $notification) {
341
            switch ($notification) {
342
                case _SF_NOT_ANSWER_APPROVED:
343
                    // This notification is not working for PM, but is for email... and I don't understand why???
344
                    $notificationHandler->triggerEvent('faq', $this->answerid(), 'answer_approved', $tags);
345
346
                    break;
347
348
                case -1:
349
                default:
350
                    break;
351
            }
352
        }
353
    }
354
}
355
356
/**
357
 * Answers handler class.
358
 * This class is responsible for providing data access mechanisms to the data source
359
 * of Answer class objects.
360
 *
361
 * @author  marcan <[email protected]>
362
 * @package SmartFAQ
363
 */
364
class sfAnswerHandler extends XoopsPersistableObjectHandler
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
365
{
366
367
    /**
368
     * create a new answer
369
     *
370
     * @param  bool $isNew flag the new objects as "new"?
371
     * @return object sfAnswer
372
     */
373
    public function create($isNew = true)
374
    {
375
        $answer = new sfAnswer();
376
        if ($isNew) {
377
            $answer->setNew();
378
        }
379
380
        return $answer;
381
    }
382
383
    /**
384
     * retrieve an answer
385
     *
386
     * @param  int $id answerid of the answer
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
387
     * @param null $fields
388
     * @return mixed reference to the <a href='psi_element://sfAnswer'>sfAnswer</a> object, FALSE if failed
389
     */
390 View Code Duplication
    public function get($id = null, $fields = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
391
    {
392
        if ((int)$id > 0) {
393
            $sql = 'SELECT * FROM ' . $this->db->prefix('smartfaq_answers') . ' WHERE answerid=' . $id;
394
            if (!$result = $this->db->query($sql)) {
395
                return false;
396
            }
397
398
            $numrows = $this->db->getRowsNum($result);
399
            if ($numrows == 1) {
400
                $answer = new sfAnswer();
401
                $answer->assignVars($this->db->fetchArray($result));
402
403
                return $answer;
404
            }
405
        }
406
407
        return false;
408
    }
409
410
    /**
411
     * insert a new answer in the database
412
     *
413
     * @param  XoopsObject $answerObj reference to the <a href='psi_element://sfAnswer'>sfAnswer</a> object
414
     * @param  bool        $force
415
     * @return bool        FALSE if failed, TRUE if already present and unchanged or successful
416
     */
417
    public function insert(XoopsObject $answerObj, $force = false)
418
    {
419
        if (strtolower(get_class($answerObj)) !== 'sfanswer') {
420
            return false;
421
        }
422
        if (!$answerObj->isDirty()) {
423
            return true;
424
        }
425
        if (!$answerObj->cleanVars()) {
426
            return false;
427
        }
428
429
        foreach ($answerObj->cleanVars as $k => $v) {
430
            ${$k} = $v;
431
        }
432
433
        if ($answerObj->isNew()) {
434
            $sql = sprintf('INSERT INTO %s (answerid, `status`, faqid, answer, uid, datesub, notifypub) VALUES (NULL, %u, %u, %s, %u, %u, %u)', $this->db->prefix('smartfaq_answers'), $status, $faqid, $this->db->quoteString($answer), $uid, time(), $notifypub);
0 ignored issues
show
Bug introduced by
The variable $status does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $faqid does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $answer does not exist. Did you mean $answerObj?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
Bug introduced by
The variable $uid does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $notifypub does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
435
        } else {
436
            $sql = sprintf('UPDATE %s SET status = %u, faqid = %s, answer = %s, uid = %u, datesub = %u, notifypub = %u WHERE answerid = %u', $this->db->prefix('smartfaq_answers'), $status, $faqid, $this->db->quoteString($answer), $uid, $datesub, $notifypub, $answerid);
0 ignored issues
show
Bug introduced by
The variable $answer does not exist. Did you mean $answerObj?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
Bug introduced by
The variable $datesub does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $answerid does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
437
        }
438
439 View Code Duplication
        if (false != $force) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
440
            $result = $this->db->queryF($sql);
441
        } else {
442
            $result = $this->db->query($sql);
443
        }
444
445
        if (!$result) {
446
            return false;
447
        }
448
449
        if ($answerObj->isNew()) {
450
            $answerObj->assignVar('answerid', $this->db->getInsertId());
451
        } else {
452
            $answerObj->assignVar('answerid', $answerid);
453
        }
454
455
        return true;
456
    }
457
458
    /**
459
     * delete an answer from the database
460
     *
461
     * @param  XoopsObject $answer reference to the answer to delete
462
     * @param  bool        $force
463
     * @return bool        FALSE if failed.
464
     */
465
    public function delete(XoopsObject $answer, $force = false)
466
    {
467
        if (strtolower(get_class($answer)) !== 'sfanswer') {
468
            return false;
469
        }
470
        $sql = sprintf('DELETE FROM %s WHERE answerid = %u', $this->db->prefix('smartfaq_answers'), $answer->getVar('answerid'));
471
472
        //echo "<br />" . $sql . "<br />";
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
473
474 View Code Duplication
        if (false != $force) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
475
            $result = $this->db->queryF($sql);
476
        } else {
477
            $result = $this->db->query($sql);
478
        }
479
        if (!$result) {
480
            return false;
481
        }
482
483
        return true;
484
    }
485
486
    /**
487
     * delete an answer from the database
488
     *
489
     * @param object $faqObj reference to the answer to delete
490
     * @return bool FALSE if failed.
491
     * @internal param bool $force
492
     */
493
    public function deleteFaqAnswers($faqObj)
494
    {
495
        if (strtolower(get_class($faqObj)) !== 'sffaq') {
496
            return false;
497
        }
498
        $answers = $this->getAllAnswers($faqObj->faqid());
499
        $result  = true;
500
        foreach ($answers as $answer) {
501
            if (!$this->delete($answer)) {
502
                $result = false;
503
            }
504
        }
505
506
        return $result;
507
    }
508
509
    /**
510
     * retrieve answers from the database
511
     *
512
     * @param  CriteriaElement $criteria  {@link CriteriaElement} conditions to be met
0 ignored issues
show
Documentation introduced by
Should the type for parameter $criteria not be null|CriteriaElement?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
513
     * @param  bool            $id_as_key use the answerid as key for the array?
514
     * @param bool             $as_object
515
     * @return array array of <a href='psi_element://sfAnswer'>sfAnswer</a> objects
516
     */
517
    public function &getObjects(CriteriaElement $criteria = null, $id_as_key = false, $as_object = true)
518
    {
519
        $ret   = array();
520
        $limit = $start = 0;
521
        $sql   = 'SELECT * FROM ' . $this->db->prefix('smartfaq_answers');
522 View Code Duplication
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
523
            $sql .= ' ' . $criteria->renderWhere();
524
            if ($criteria->getSort() != '') {
525
                $sql .= ' ORDER BY ' . $criteria->getSort() . ' ' . $criteria->getOrder();
526
            }
527
            $limit = $criteria->getLimit();
528
            $start = $criteria->getStart();
529
        }
530
        //echo "<br />" . $sql . "<br />";
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
531
        $result = $this->db->query($sql, $limit, $start);
532
        if (!$result) {
533
            return $ret;
534
        }
535 View Code Duplication
        while ($myrow = $this->db->fetchArray($result)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
536
            $answer = new sfAnswer();
537
            $answer->assignVars($myrow);
538
            if (!$id_as_key) {
539
                $ret[] =& $answer;
540
            } else {
541
                $ret[$myrow['answerid']] =& $answer;
542
            }
543
            unset($answer);
544
        }
545
546
        return $ret;
547
    }
548
549
    /**
550
     * retrieve 1 official answer (for now SmartFAQ only allow 1 official answer...)
551
     *
552
     * @param  int $faqid
553
     * @return mixed reference to the <a href='psi_element://sfAnswer'>sfAnswer</a> object, FALSE if failed
554
     */
555
    public function getOfficialAnswer($faqid = 0)
556
    {
557
        $theaAnswers = $this->getAllAnswers($faqid, _SF_AN_STATUS_APPROVED, 1, 0);
558
        $ret         = false;
559
        if (count($theaAnswers) == 1) {
560
            $ret = $theaAnswers[0];
561
        }
562
563
        return $ret;
564
    }
565
566
    /**
567
     * retrieve all answers
568
     *
569
     * @param  int    $faqid
570
     * @param  int    $status
571
     * @param  int    $limit
572
     * @param  int    $start
573
     * @param  string $sort
574
     * @param  string $order
575
     * @return array  array of <a href='psi_element://sfAnswer'>sfAnswer</a> objects
576
     */
577
    public function getAllAnswers($faqid = 0, $status = -1, $limit = 0, $start = 0, $sort = 'datesub', $order = 'DESC')
578
    {
579
        $hasStatusCriteria = false;
580
        $criteriaStatus    = new CriteriaCompo();
581
        if (is_array($status)) {
582
            $hasStatusCriteria = true;
583
            foreach ($status as $v) {
584
                $criteriaStatus->add(new Criteria('status', $v), 'OR');
585
            }
586
        } elseif ($status != -1) {
587
            $hasStatusCriteria = true;
588
            $criteriaStatus->add(new Criteria('status', $status), 'OR');
589
        }
590
        $criteriaFaqid = new Criteria('faqid', $faqid);
591
592
        $criteria = new CriteriaCompo();
593
        $criteria->add($criteriaFaqid);
594
595
        if ($hasStatusCriteria) {
596
            $criteria->add($criteriaStatus);
597
        }
598
599
        $criteria->setSort($sort);
600
        $criteria->setOrder($order);
601
        $criteria->setLimit($limit);
602
        $criteria->setStart($start);
603
        $ret =& $this->getObjects($criteria);
604
605
        return $ret;
606
    }
607
608
    /**
609
     * count answers matching a condition
610
     *
611
     * @param  CriteriaElement $criteria {@link CriteriaElement} to match
0 ignored issues
show
Documentation introduced by
Should the type for parameter $criteria not be null|CriteriaElement?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
612
     * @return int             count of answers
613
     */
614 View Code Duplication
    public function getCount(CriteriaElement $criteria = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
615
    {
616
        $sql = 'SELECT COUNT(*) FROM ' . $this->db->prefix('smartfaq_answers');
617
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
618
            $sql .= ' ' . $criteria->renderWhere();
619
        }
620
        $result = $this->db->query($sql);
621
        if (!$result) {
622
            return 0;
623
        }
624
        list($count) = $this->db->fetchRow($result);
625
626
        return $count;
627
    }
628
629
    /**
630
     * count answers matching a condition and group by faq ID
631
     *
632
     * @param  object $criteria {@link CriteriaElement} to match
0 ignored issues
show
Documentation introduced by
Should the type for parameter $criteria not be object|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
633
     * @return array
634
     */
635
    public function getCountByFAQ($criteria = null)
636
    {
637
        $sql = 'SELECT faqid, COUNT(*) FROM ' . $this->db->prefix('smartfaq_answers');
638
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
639
            $sql .= ' ' . $criteria->renderWhere();
640
            $sql .= ' ' . $criteria->getGroupby();
641
        }
642
643
        //echo "<br />$sql<br />";
644
645
        $result = $this->db->query($sql);
646
        if (!$result) {
647
            return array();
648
        }
649
        $ret = array();
650
        while (list($id, $count) = $this->db->fetchRow($result)) {
651
            $ret[$id] = $count;
652
        }
653
654
        return $ret;
655
    }
656
657
    /**
658
     * delete answers matching a set of conditions
659
     *
660
     * @param  CriteriaElement $criteria {@link CriteriaElement}
0 ignored issues
show
Documentation introduced by
Should the type for parameter $criteria not be null|CriteriaElement?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
661
     * @param bool             $force
662
     * @param bool             $asObject
663
     * @return bool FALSE if deletion failed
664
     */
665
    public function deleteAll(CriteriaElement $criteria = null, $force = true, $asObject = false)
666
    {
667
        $sql = 'DELETE FROM ' . $this->db->prefix('smartfaq_answers');
668
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
669
            $sql .= ' ' . $criteria->renderWhere();
670
        }
671
        if (!$this->db->query($sql)) {
672
            return false;
673
        }
674
675
        return true;
676
    }
677
678
    /**
679
     * Change a value for answers with a certain criteria
680
     *
681
     * @param string          $fieldname  Name of the field
682
     * @param string          $fieldvalue Value to write
683
     * @param CriteriaElement $criteria   {@link CriteriaElement}
0 ignored issues
show
Documentation introduced by
Should the type for parameter $criteria not be null|CriteriaElement?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
684
     * @param bool            $force
685
     * @return bool
686
     */
687 View Code Duplication
    public function updateAll($fieldname, $fieldvalue, CriteriaElement $criteria = null, $force = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
688
    {
689
        $set_clause = is_numeric($fieldvalue) ? $fieldname . ' = ' . $fieldvalue : $fieldname . ' = ' . $this->db->quoteString($fieldvalue);
690
        $sql        = 'UPDATE ' . $this->db->prefix('smartfaq_answers') . ' SET ' . $set_clause;
691
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
692
            $sql .= ' ' . $criteria->renderWhere();
693
        }
694
        //echo "<br />" . $sql . "<br />";
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
695
        if (!$this->db->queryF($sql)) {
696
            return false;
697
        }
698
699
        return true;
700
    }
701
702
    /**
703
     * @param $faqids
704
     * @return array
705
     */
706
    public function getLastPublishedByFaq($faqids)
707
    {
708
        $ret    = array();
709
        $sql    = 'SELECT faqid, answer, uid, datesub FROM ' . $this->db->prefix('smartfaq_answers') . '
710
               WHERE faqid IN (' . implode(',', $faqids) . ') AND status = ' . _SF_AN_STATUS_APPROVED . ' GROUP BY faqid';
711
        $result = $this->db->query($sql);
712
        if (!$result) {
713
            return $ret;
714
        }
715 View Code Duplication
        while ($row = $this->db->fetchArray($result)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
716
            $answer = new sfAnswer();
717
            $answer->assignVars($row);
718
            $ret[$row['faqid']] =& $answer;
719
            unset($answer);
720
        }
721
722
        return $ret;
723
    }
724
}
725