/** * Send the comment and attached media to database * * @param Media $media * @param array $data extra data * @throws CommentSendingDatabaseException * @throws CommentSendingBannedException * @throws CommentSendingThreadWithoutMediaException * @throws CommentSendingSpamException * @throws CommentSendingImageInGhostException * @throws CommentSendingNoDelPassException * @throws CommentSendingThreadClosedException * @throws CommentSendingRequestCaptchaException * @throws CommentSendingTimeLimitException * @throws CommentSendingException * @throws CommentSendingTooManyCharactersException * @throws \RuntimeException * @throws CommentSendingDisplaysEmptyException * @throws CommentSendingTooManyLinesException * @throws CommentSendingSameCommentException * @throws CommentSendingWrongCaptchaException * @throws CommentSendingUnallowedCapcodeException * @return array error key with explanation to show to user, or success and post row */ public function p_insert(Media $media = null, $data = []) { if (isset($data['recaptcha_challenge'])) { $this->recaptcha_challenge = $data['recaptcha_challenge']; $this->recaptcha_response = $data['recaptcha_response']; } $this->ghost = false; $this->ghost_exist = false; $this->allow_media = true; // some users don't need to be limited, in here go all the ban and posting limitators if (!$this->getAuth()->hasAccess('comment.limitless_comment')) { // check if the user is banned if ($ban = $this->ban_factory->isBanned($this->comment->poster_ip, $this->radix)) { if ($ban->board_id == 0) { $banned_string = _i('It looks like you were banned on all boards.'); } else { $banned_string = _i('It looks like you were banned on /' . $this->radix->shortname . '/.'); } if ($ban->length) { $banned_string .= ' ' . _i('This ban will last until:') . ' ' . date(DATE_COOKIE, $ban->start + $ban->length) . '.'; } else { $banned_string .= ' ' . _i('This ban will last forever.'); } if ($ban->reason) { $banned_string .= ' ' . _i('The reason for this ban is:') . ' «' . $ban->reason . '».'; } if ($ban->appeal_status == Ban::APPEAL_NONE) { $banned_string .= ' ' . _i('If you\'d like to appeal to your ban, go to the %s page.', '<a href="' . $this->uri->create($this->radix->shortname . '/appeal') . '">' . _i('Appeal') . '</a>'); } elseif ($ban->appeal_status == Ban::APPEAL_PENDING) { $banned_string .= ' ' . _i('Your appeal is pending.'); } throw new CommentSendingBannedException($banned_string); } } // check if it's a thread and its status if ($this->comment->thread_num > 0) { try { $thread = Board::forge($this->getContext())->getThread($this->comment->thread_num)->setRadix($this->radix); $status = $thread->getThreadStatus(); } catch (BoardException $e) { throw new CommentSendingException($e->getMessage()); } if ($status['closed']) { throw new CommentSendingThreadClosedException(_i('The thread is closed.')); } $this->ghost = $status['dead']; $this->ghost_exist = $status['ghost_exist']; $this->allow_media = !$status['disable_image_upload']; } foreach (['name', 'email', 'title', 'comment', 'capcode'] as $key) { $this->comment->{$key} = trim((string) $this->comment->{$key}); } $this->comment->setDelpass(trim((string) $this->comment->getDelPass())); // some users don't need to be limited, in here go all the ban and posting limitators if (!$this->getAuth()->hasAccess('comment.limitless_comment')) { if ($this->comment->thread_num < 1) { // one can create a new thread only once every 5 minutes $check_op = $this->dc->qb()->select('*')->from($this->radix->getTable(), 'r')->where('r.poster_ip = :poster_ip')->andWhere('r.timestamp > :timestamp')->andWhere('r.op = :op')->setParameters([':poster_ip' => $this->comment->poster_ip, ':timestamp' => time() - $this->radix->getValue('cooldown_new_thread'), ':op' => true])->setMaxResults(1)->execute()->fetch(); if ($check_op) { throw new CommentSendingTimeLimitException(_i('You must wait up to %d minutes to make another new thread.', ceil($this->radix->getValue('cooldown_new_thread') / 60))); } } // check the latest posts by the user to see if he's posting the same message or if he's posting too fast $check = $this->dc->qb()->select('*')->from($this->radix->getTable(), 'r')->where('poster_ip = :poster_ip')->orderBy('timestamp', 'DESC')->setMaxResults(1)->setParameter(':poster_ip', $this->comment->poster_ip)->execute()->fetch(); if ($check) { if ($this->comment->comment !== null && $check['comment'] === $this->comment->comment) { throw new CommentSendingSameCommentException(_i('You\'re sending the same comment as the last time')); } $check_time = $this->getRadixTime(); if ($check_time - $check['timestamp'] < $this->radix->getValue('cooldown_new_comment') && $check_time - $check['timestamp'] > 0) { throw new CommentSendingTimeLimitException(_i('You must wait up to %d seconds to post again.', $this->radix->getValue('cooldown_new_comment'))); } } // we want to know if the comment will display empty, and in case we won't let it pass $comment_parsed = $this->processComment(); if ($this->comment->comment !== '' && $comment_parsed === '') { throw new CommentSendingDisplaysEmptyException(_i('This comment would display empty.')); } // clean up to reset eventual auto-built entries $this->comment->clean(); if ($this->recaptcha_challenge && $this->recaptcha_response && $this->preferences->get('foolframe.auth.recaptcha_public', false)) { $recaptcha = ReCaptcha::create($this->preferences->get('foolframe.auth.recaptcha_public'), $this->preferences->get('foolframe.auth.recaptcha_private')); $recaptcha_result = $recaptcha->checkAnswer(Inet::dtop($this->comment->poster_ip), $this->recaptcha_challenge, $this->recaptcha_response); if (!$recaptcha_result->isValid()) { throw new CommentSendingWrongCaptchaException(_i('Incorrect CAPTCHA solution.')); } } elseif ($this->preferences->get('foolframe.auth.recaptcha_public')) { // if there wasn't a recaptcha input, let's go with heavier checks if (substr_count($this->comment->comment, 'http') >= $this->radix->getValue('captcha_comment_link_limit')) { throw new CommentSendingRequestCaptchaException(); } // bots usually fill all the fields if ($this->comment->comment && $this->comment->title && $this->comment->email) { throw new CommentSendingRequestCaptchaException(); } // bots usually try various BBC, this checks if there's unparsed BBC after parsing it if ($comment_parsed !== '' && substr_count($comment_parsed, '[') + substr_count($comment_parsed, ']') > 4) { throw new CommentSendingRequestCaptchaException(); } } // load the spam list and check comment, name, title and email $spam = array_filter(preg_split('/\\r\\n|\\r|\\n/', file_get_contents(ASSETSPATH . 'packages/anti-spam/databases/urls.dat'))); foreach ($spam as $s) { if (strpos($this->comment->comment, $s) !== false || strpos($this->comment->name, $s) !== false || strpos($this->comment->title, $s) !== false || strpos($this->comment->email, $s) !== false) { throw new CommentSendingSpamException(_i('Your post has undesidered content.')); } } // check entire length of comment if (mb_strlen($this->comment->comment, 'utf-8') > $this->radix->getValue('max_comment_characters_allowed')) { throw new CommentSendingTooManyCharactersException(_i('Your comment has too many characters')); } // check total numbers of lines in comment if (count(explode("\n", $this->comment->comment)) > $this->radix->getValue('max_comment_lines_allowed')) { throw new CommentSendingTooManyLinesException(_i('Your comment has too many lines.')); } } \Foolz\Plugin\Hook::forge('Foolz\\Foolslide\\Model\\CommentInsert::insert.call.after.input_checks')->setObject($this)->execute(); // process comment name+trip if ($this->comment->name === '') { $this->comment->name = $this->radix->getValue('anonymous_default_name'); $this->comment->trip = null; } else { $this->processName(); if ($this->comment->trip === '') { $this->comment->trip = null; } } foreach (['email', 'title', 'comment'] as $key) { if ($this->comment->{$key} === '') { $this->comment->{$key} = null; } } // process comment password if ($this->comment->getDelpass() === '') { throw new CommentSendingNoDelPassException(_i('You must submit a deletion password.')); } $pass = password_hash($this->comment->getDelpass(), PASSWORD_BCRYPT, ['cost' => 10]); if ($this->comment->getDelpass() === false) { throw new \RuntimeException('Password hashing failed'); } $this->comment->setDelpass($pass); if ($this->comment->capcode != '') { $allowed_capcodes = ['N']; if ($this->getAuth()->hasAccess('comment.mod_capcode')) { $allowed_capcodes[] = 'M'; } if ($this->getAuth()->hasAccess('comment.admin_capcode')) { $allowed_capcodes[] = 'A'; } if ($this->getAuth()->hasAccess('comment.dev_capcode')) { $allowed_capcodes[] = 'D'; } if (!in_array($this->comment->capcode, $allowed_capcodes)) { throw new CommentSendingUnallowedCapcodeException(_i('You\'re not allowed to use this capcode.')); } } else { $this->comment->capcode = 'N'; } $microtime = str_replace('.', '', (string) microtime(true)); $this->comment->timestamp = substr($microtime, 0, 10); $this->comment->op = (bool) (!$this->comment->thread_num); if ($this->radix->getValue('enable_flags')) { $reader = new Reader($this->preferences->get('foolframe.maxmind.geoip2_db_path')); try { $record = $reader->country(Inet::dtop($this->comment->poster_ip)); $this->comment->poster_country = strtolower($record->country->isoCode); } catch (AddressNotFoundException $e) { $this->comment->poster_country = 'xx'; } } // process comment media if ($media !== null) { // if uploading an image with OP is prohibited if (!$this->comment->thread_num && $this->radix->getValue('op_image_upload_necessity') === 'never') { throw new CommentSendingException(_i('You can\'t start a new thread with an image.')); } if (!$this->allow_media) { if ($this->ghost) { throw new CommentSendingImageInGhostException(_i('You can\'t post images when the thread is in ghost mode.')); } else { throw new CommentSendingException(_i('This thread has reached its image limit.')); } } try { $media->insert($microtime, $this->comment->op); $this->media = $media->media; } catch (MediaInsertException $e) { throw new CommentSendingException($e->getMessage()); } } else { // if the user is forced to upload an image when making a new thread if (!$this->comment->thread_num && $this->radix->getValue('op_image_upload_necessity') === 'always') { throw new CommentSendingThreadWithoutMediaException(_i('You can\'t start a new thread without an image.')); } // in case of no media, check comment field again for null if ($this->comment->comment === null) { throw new CommentSendingDisplaysEmptyException(_i('This comment would display empty.')); } $this->media = $this->bulk->media = new MediaData(); } // 2ch-style codes, only if enabled if ($this->comment->thread_num && $this->radix->getValue('enable_poster_hash')) { $this->comment->poster_hash = substr(substr(crypt(md5($this->comment->poster_ip . 'id' . $this->comment->thread_num), 'id'), +3), 0, 8); } $this->comment->timestamp = $this->getRadixTime($this->comment->timestamp); \Foolz\Plugin\Hook::forge('Foolz\\Foolslide\\Model\\CommentInsert::insert.call.before.sql')->setObject($this)->execute(); // being processing insert... if ($this->ghost) { $num = ' ( SELECT MAX(num) AS num FROM ( ( SELECT num FROM ' . $this->radix->getTable() . ' xr WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' ) UNION ( SELECT num FROM ' . $this->radix->getTable('_deleted') . ' xrd WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' ) ) x )'; $subnum = ' ( SELECT MAX(subnum) + 1 AS subnum FROM ( ( SELECT subnum FROM ' . $this->radix->getTable() . ' xxr WHERE num = ( SELECT MAX(num) FROM ( ( SELECT num FROM ' . $this->radix->getTable() . ' xxxr WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' ) UNION ( SELECT num FROM ' . $this->radix->getTable('_deleted') . ' xxxrd WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' ) ) xxx ) ) UNION ( SELECT subnum FROM ' . $this->radix->getTable('_deleted') . ' xxdr WHERE num = ( SELECT MAX(num) FROM ( ( SELECT num FROM ' . $this->radix->getTable() . ' xxxr WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' ) UNION ( SELECT num FROM ' . $this->radix->getTable('_deleted') . ' xxxdrd WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' ) ) xxxd ) ) ) xx )'; $thread_num = $this->comment->thread_num; } else { $num = ' ( SELECT MAX(num) + 1 AS num FROM ( ( SELECT COALESCE(MAX(num), 0) AS num FROM ' . $this->radix->getTable() . ' xr ) UNION ( SELECT COALESCE(MAX(num), 0) AS num FROM ' . $this->radix->getTable('_deleted') . ' xdr ) ) x )'; $subnum = 0; if ($this->comment->thread_num > 0) { $thread_num = $this->dc->getConnection()->quote($this->comment->thread_num); } else { $thread_num = ' ( SELECT MAX(thread_num) + 1 AS thread_num FROM ( ( SELECT COALESCE(MAX(num), 0) as thread_num FROM ' . $this->radix->getTable() . ' xxr ) UNION ( SELECT COALESCE(MAX(num), 0) as thread_num FROM ' . $this->radix->getTable('_deleted') . ' xxdr ) ) xx )'; } } try { $query_fields = ['num' => $num, 'subnum' => $subnum, 'thread_num' => $thread_num]; $fields = ['media_id' => $this->media->media_id ? $this->media->media_id : 0, 'op' => (int) $this->op, 'timestamp' => $this->comment->timestamp, 'capcode' => $this->comment->capcode, 'email' => $this->comment->email, 'name' => $this->comment->name, 'trip' => $this->comment->trip, 'title' => $this->comment->title, 'comment' => $this->comment->comment, 'delpass' => $this->comment->getDelpass(), 'spoiler' => (int) $this->media->spoiler, 'poster_ip' => $this->comment->poster_ip, 'poster_hash' => $this->comment->poster_hash, 'poster_country' => $this->comment->poster_country, 'preview_orig' => $this->media->preview_orig, 'preview_w' => $this->media->preview_w, 'preview_h' => $this->media->preview_h, 'media_filename' => $this->media->media_filename, 'media_w' => $this->media->media_w, 'media_h' => $this->media->media_h, 'media_size' => $this->media->media_size, 'media_hash' => $this->media->media_hash, 'media_orig' => $this->media->media_orig, 'exif' => $this->media->exif !== null ? @json_encode($this->media->exif) : null, 'timestamp_expired' => 0]; foreach ($fields as $key => $item) { if ($item === null) { $fields[$key] = 'null'; } else { $fields[$key] = $this->dc->getConnection()->quote($item); } } $fields = $query_fields + $fields; $this->dc->getConnection()->beginTransaction(); $this->dc->getConnection()->executeUpdate('INSERT INTO ' . $this->radix->getTable() . ' (' . implode(', ', array_keys($fields)) . ') VALUES (' . implode(', ', array_values($fields)) . ')'); $last_id = $this->dc->getConnection()->lastInsertId($this->radix->getTable('_doc_id_seq')); $comment = $this->dc->qb()->select('*')->from($this->radix->getTable(), 'r')->where('doc_id = :doc_id')->setParameter(':doc_id', $last_id)->execute()->fetchAll(); $this->bulk->import($comment[0], $this->radix); if (!$this->radix->archive) { $this->insertTriggerThreads(); $this->insertTriggerDaily(); $this->insertTriggerUsers(); } // update poster_hash for op posts if ($this->comment->op && $this->radix->getValue('enable_poster_hash')) { $this->comment->poster_hash = substr(substr(crypt(md5($this->comment->poster_ip . 'id' . $this->comment->thread_num), 'id'), 3), 0, 8); $this->dc->qb()->update($this->radix->getTable(), 'ph')->set('poster_hash', $this->dc->getConnection()->quote($this->comment->poster_hash))->where('doc_id = :doc_id')->setParameter(':doc_id', $this->comment->doc_id)->execute(); } $this->dc->getConnection()->commit(); // clean up some caches Cache::item('foolslide.model.board.getThreadComments.thread.' . md5(serialize([$this->radix->shortname, $this->comment->thread_num])))->delete(); // clean up the 10 first pages of index and gallery that are cached for ($i = 1; $i <= 10; $i++) { Cache::item('foolslide.model.board.getLatestComments.query.' . $this->radix->shortname . '.by_post.' . $i)->delete(); Cache::item('foolslide.model.board.getLatestComments.query.' . $this->radix->shortname . '.by_thread.' . $i)->delete(); Cache::item('foolslide.model.board.getThreadsComments.query.' . $this->radix->shortname . '.' . $i)->delete(); } } catch (\Doctrine\DBAL\DBALException $e) { $this->logger->error('\\Foolz\\Foolslide\\Model\\CommentInsert: ' . $e->getMessage()); $this->dc->getConnection()->rollBack(); throw new CommentSendingDatabaseException(_i('Something went wrong when inserting the post in the database. Try again.')); } return $this; }
/** * Takes an uploaded file and makes an object. It doesn't do the ->insert() * * @param Radix $radix The Radix where this Media belongs * * @return \Foolz\Foolslide\Model\Media A new Media object with the upload data * @throws MediaUploadNoFileException If there's no file uploaded * @throws MediaUploadMultipleNotAllowedException If there's multiple uploads * @throws MediaUploadInvalidException If the file format is not allowed */ protected function p_forgeFromUpload(Request $request, Radix $radix) { $config = Hook::forge('Foolz\\Foolslide\\Model\\Media::upload.config')->setParams(['ext_whitelist' => ['jpg', 'jpeg', 'gif', 'png'], 'mime_whitelist' => ['image/jpeg', 'image/png', 'image/gif']])->execute()->getParams(); if (!$request->files->count()) { throw new MediaUploadNoFileException(_i('You must upload an image or your image was too large.')); } if ($request->files->count() !== 1) { throw new MediaUploadMultipleNotAllowedException(_i('You can\'t upload multiple images.')); } /** @var UploadedFile[] $files */ $files = $request->files->all(); $max_size = $radix->getValue('max_image_size_kilobytes') * 1024; foreach ($files as $file) { if (!$file->isValid()) { if ($file->getError() === UPLOAD_ERR_INI_SIZE) { throw new MediaUploadInvalidException(_i('The server is misconfigured: the Foolslide upload size should be lower than PHP\'s upload limit.')); } if ($file->getError() === UPLOAD_ERR_PARTIAL) { throw new MediaUploadInvalidException(_i('You uploaded the file partially.')); } if ($file->getError() === UPLOAD_ERR_CANT_WRITE) { throw new MediaUploadInvalidException(_i('The image couldn\'t be saved on the disk.')); } if ($file->getError() === UPLOAD_ERR_EXTENSION) { throw new MediaUploadInvalidException(_i('A PHP extension broke and made processing the image impossible.')); } throw new MediaUploadInvalidException(_i('Unexpected upload error.')); } if (mb_strlen($file->getFilename(), 'utf-8') > 64) { throw new MediaUploadInvalidException(_i('You uploaded a file with a too long filename.')); } if (!in_array(strtolower($file->getClientOriginalExtension()), $config['ext_whitelist'])) { throw new MediaUploadInvalidException(_i('You uploaded a file with an invalid extension.')); } if (!in_array(strtolower($file->getMimeType()), $config['mime_whitelist'])) { throw new MediaUploadInvalidException(_i('You uploaded a file with an invalid mime type.')); } if ($file->getClientSize() > $max_size && !$this->getAuth()->hasAccess('media.limitless_media')) { throw new MediaUploadInvalidException(_i('You uploaded a too big file. The maxmimum allowed filesize is %s', $radix->getValue('max_image_size_kilobytes'))); } } $media = new Media($this->getContext()); $media->setTempFile($radix, $files['file_image']); return $media; }
public function get_search() { // check all allowed search modifiers and apply only these $modifiers = ['boards', 'subject', 'text', 'username', 'tripcode', 'email', 'filename', 'capcode', 'uid', 'image', 'deleted', 'ghost', 'type', 'filter', 'start', 'end', 'order', 'page']; if ($this->getAuth()->hasAccess('comment.see_ip')) { $modifiers[] = 'poster_ip'; } $search = []; foreach ($modifiers as $modifier) { $search[$modifier] = $this->getQuery($modifier, null); } foreach ($search as $key => $value) { if (in_array($key, $modifiers) && $value !== null) { $search[$key] = trim(rawurldecode($value)); } } if ($search['boards'] !== null) { $search['boards'] = explode('.', $search['boards']); } if ($search['image'] !== null) { $search['image'] = base64_encode(Media::urlsafe_b64decode($search['image'])); } if ($this->getAuth()->hasAccess('comment.see_ip') && $search['poster_ip'] !== null) { if (!filter_var($search['poster_ip'], FILTER_VALIDATE_IP)) { return $this->response->setData(['error' => _i('The poster IP you inserted is not a valid IP address.')]); } $search['poster_ip'] = Inet::ptod($search['poster_ip']); } try { $board = Search::forge($this->getContext())->getSearch($search)->setRadix($this->radix)->setPage($search['page'] ? $search['page'] : 1); foreach ($board->getCommentsUnsorted() as $comment) { $this->apify($comment); } $comments = $board->getComments(); $this->response->setData($comments); } catch (\Foolz\Foolslide\Model\SearchException $e) { return $this->response->setData(['error' => $e->getMessage()]); } catch (\Foolz\Foolslide\Model\BoardException $e) { return $this->response->setData(['error' => $e->getMessage()])->setStatusCode(500); } return $this->response; }
foreach ($data_array as $key => $item) { ?> <div class="image_reposts_image"> <div class="image_reposts_number"> <strong>#<?php echo $key + 1; ?> </strong> - Reposts: <?php echo $item->total; ?> </div> <?php $bulk = new CommentBulk(); $bulk->import((array) $item, $this->radix); $media = new Media($this->getContext(), $bulk); $media->op = true; ?> <a href="<?php echo $this->uri->create([$this->radix->shortname, 'search', 'image', $media->getSafeMediaHash()]); ?> "> <img src="<?php echo $media->getThumbLink($this->getRequest()); ?> " /> </a> </div> <?php }
public function toString() { $board = $this->getParamManager()->getParam('board'); $controller_method = $this->getBuilderParamManager()->getParam('controller_method', 'thread'); $thread_id = $this->getBuilderParamManager()->getParam('thread_id', 0); foreach ($board as $key => $post) { if (isset($post['op'])) { $op_bulk = $post['op']; $op = new Comment($this->getContext(), $op_bulk); $op->setControllerMethod($controller_method); if ($op_bulk->media !== null) { $op_media = new Media($this->getContext(), $op_bulk); } else { $op_media = null; } $num = $op->num . ($op->subnum ? '_' . $op->subnum : ''); ?> <?php if ($thread_id === 0) { ?> <div class="thread stub stub_doc_id_<?php echo $op->doc_id; ?> "> <button class="btn-toggle-post" data-function="showThread" data-board="<?php echo $op->radix->shortname; ?> " data-doc-id="<?php echo $op->doc_id; ?> " data-thread-num="<?php echo $op->thread_num; ?> "><i class="icon-plus"></i></button> <?php if ($op->email && $op->email !== 'noko') { ?> <a href="mailto:<?php echo rawurlencode($op->email); ?> "><?php } ?> <span class="post_author"><?php echo $op->getNameProcessed(); ?> </span><?php echo $op->getNameProcessed() && $op->getTripProcessed() ? ' ' : ''; ?> <span class="post_tripcode"><?php echo $op->getTripProcessed(); ?> </span><?php if ($op->email && $op->email !== 'noko') { ?> </a><?php } ?> (<?php echo $post['omitted'] + 5 . ' ' . _i('replies'); ?> ) </div> <?php } ?> <article id="<?php echo $num; ?> " class="clearfix thread doc_id_<?php echo $op->doc_id; ?> board_<?php echo $op->radix->shortname; ?> " data-doc-id="<?php echo $op->doc_id; ?> " data-thread-num="<?php echo $op->thread_num; ?> "> <?php if ($thread_id === 0) { ?> <div class="stub pull-left"> <button class="btn-toggle-post" data-function="hideThread" data-board="<?php echo $op->radix->shortname; ?> " data-doc-id="<?php echo $op->doc_id; ?> "><i class="icon-minus"></i></button> </div> <?php } ?> <?php \Foolz\Plugin\Hook::forge('foolslide.themes.default_after_op_open')->setObject($this)->setParam('board', $op->radix)->execute(); ?> <?php if ($op_media !== null) { ?> <div class="thread_image_box"> <?php if ($op_media->getMediaStatus($this->getRequest()) === 'banned') { ?> <img src="<?php echo $this->getAssetManager()->getAssetLink('images/banned-image.png'); ?> " width="150" height="150" /> <?php } elseif ($op_media->getMediaStatus($this->getRequest()) !== 'normal') { ?> <a href="<?php echo $op_media->getMediaLink($this->getRequest()) ? $op_media->getMediaLink($this->getRequest()) : $op_media->getRemoteMediaLink($this->getRequest()); ?> " target="_blank" rel="noreferrer" class="thread_image_link"> <img src="<?php echo $this->getAssetManager()->getAssetLink('images/missing-image.jpg'); ?> " width="150" height="150" /> </a> <?php } else { ?> <a href="<?php echo $op_media->getMediaLink($this->getRequest()) ? $op_media->getMediaLink($this->getRequest()) : $op_media->getRemoteMediaLink($this->getRequest()); ?> " target="_blank" rel="noreferrer" class="thread_image_link"> <?php if (!$this->getAuth()->hasAccess('maccess.mod') && !$op->radix->getValue('transparent_spoiler') && $op_media->spoiler) { ?> <div class="spoiler_box"><span class="spoiler_box_text"><?php echo _i('Spoiler'); ?> <span class="spoiler_box_text_help"><?php echo _i('Click to view'); ?> </span></div> <?php } else { ?> <img src="<?php echo $op_media->getThumbLink($this->getRequest()); ?> " width="<?php echo $op_media->preview_w; ?> " height="<?php echo $op_media->preview_h; ?> " class="thread_image<?php echo $op_media->spoiler ? ' is_spoiler_image' : ''; ?> " data-md5="<?php echo $op_media->media_hash; ?> " /> <?php } ?> </a> <?php } ?> <?php if ($op_media->getMediaStatus($this->getRequest()) !== 'banned') { ?> <div class="post_file" style="padding-left: 2px;<?php if ($op_media->preview_w > 149) { echo 'max-width:' . $op_media->preview_w . 'px;'; } ?> "> <?php echo ByteSize::formatBinary($op_media->media_size, 0) . ', ' . $op_media->media_w . 'x' . $op_media->media_h . ', '; ?> <a class="post_file_filename" href="<?php echo $op_media->getMediaLink($this->getRequest()) ? $op_media->getMediaLink($this->getRequest()) : $op_media->getRemoteMediaLink($this->getRequest()); ?> " target="_blank"><?php echo $op_media->getMediaFilenameProcessed(); ?> </a> </div> <?php } ?> <div class="post_file_controls"> <?php if ($op_media->getMediaStatus($this->getRequest()) !== 'banned' || $this->getAuth()->hasAccess('media.see_banned')) { ?> <?php if (!$op->radix->hide_thumbnails || $this->getAuth()->hasAccess('maccess.mod')) { ?> <a href="<?php echo $this->getUri()->create($op->radix->shortname . '/search/image/' . $op_media->getSafeMediaHash()); ?> " class="btnr parent"><?php echo _i('View Same'); ?> </a><a href="http://google.com/searchbyimage?image_url=<?php echo $op_media->getThumbLink($this->getRequest()); ?> " target="_blank" class="btnr parent">Google</a><a href="http://iqdb.org/?url=<?php echo $op_media->getThumbLink($this->getRequest()); ?> " target="_blank" class="btnr parent">iqdb</a><a href="http://saucenao.com/search.php?url=<?php echo $op_media->getThumbLink($this->getRequest()); ?> " target="_blank" class="btnr parent">SauceNAO</a><?php if (!$op->radix->archive || $op->radix->getValue('archive_full_images')) { ?> <a href="<?php echo $op_media->getMediaDownloadLink($this->getRequest()); ?> " download="<?php echo $op_media->getMediaFilenameProcessed(); ?> " class="btnr parent"><i class="icon-download-alt"></i></a><?php } ?> <?php } ?> <?php } ?> </div> </div> <?php } ?> <header> <div class="post_data"> <?php if ($op->getTitleProcessed() !== '') { ?> <h2 class="post_title"><?php echo $op->getTitleProcessed(); ?> </h2><?php } ?> <span class="post_poster_data"> <?php if ($op->email && $op->email !== 'noko') { ?> <a href="mailto:<?php echo rawurlencode($op->email); ?> "><?php } ?> <span class="post_author"><?php echo $op->getNameProcessed(); ?> </span><?php echo $op->getNameProcessed() && $op->getTripProcessed() ? ' ' : ''; ?> <span class="post_tripcode"><?php echo $op->getTripProcessed(); ?> </span><?php if ($op->email && $op->email !== 'noko') { ?> </a><?php } ?> <?php if ($op->getPosterHashProcessed()) { ?> <span class="poster_hash">ID:<?php echo $op->getPosterHashProcessed(); ?> </span><?php } ?> <?php if ($op->capcode !== 'N') { ?> <?php if ($op->capcode === 'M') { ?> <span class="post_level post_level_moderator">## <?php echo _i('Mod'); ?> </span><?php } ?> <?php if ($op->capcode === 'A') { ?> <span class="post_level post_level_administrator">## <?php echo _i('Admin'); ?> </span><?php } ?> <?php if ($op->capcode === 'D') { ?> <span class="post_level post_level_developer">## <?php echo _i('Developer'); ?> </span><?php } ?> <?php } ?> </span> <span class="time_wrap"> <time datetime="<?php echo gmdate(DATE_W3C, $op->timestamp); ?> " class="show_time" <?php if ($op->radix->archive) { ?> title="<?php echo _i('4chan Time') . ': ' . $op->getFourchanDate(); ?> "<?php } ?> ><?php echo gmdate('D d M H:i:s Y', $op->timestamp); ?> </time> </span> <a href="<?php echo $this->getUri()->create(array($op->radix->shortname, $controller_method, $op->thread_num)) . '#' . $num; ?> " data-post="<?php echo $num; ?> " data-function="highlight">No.</a><a href="<?php echo $this->getUri()->create(array($op->radix->shortname, $controller_method, $op->thread_num)) . '#q' . $num; ?> " data-post="<?php echo $num; ?> " data-function="quote"><?php echo $num; ?> </a> <span class="post_type"> <?php if ($op->poster_country !== null) { ?> <span title="<?php echo e($op->poster_country_name); ?> " class="flag flag-<?php echo strtolower($op->poster_country); ?> "></span><?php } ?> <?php if (isset($op_media) && $op_media->spoiler) { ?> <i class="icon-eye-close" title="<?php echo htmlspecialchars(_i('The image in this post has been marked spoiler.')); ?> "></i><?php } ?> <?php if ($op->deleted && !$op->timestamp_expired) { ?> <i class="icon-trash" title="<?php echo htmlspecialchars(_i('This thread was prematurely deleted.')); ?> "></i><?php } ?> <?php if ($op->deleted && $op->timestamp_expired) { ?> <i class="icon-trash" title="<?php echo htmlspecialchars(_i('This thread was deleted on %s.', gmdate('M d, Y \\a\\t H:i:s e', $op->timestamp_expired))); ?> "></i><?php } ?> <?php if ($op->sticky) { ?> <i class="icon-pushpin" title="<?php echo _i('This thread has been stickied.'); ?> "></i><?php } ?> <?php if ($op->locked) { ?> <i class="icon-lock" title="<?php echo _i('This thread has been locked.'); ?> "></i><?php } ?> </span> <span class="post_controls"> <a href="<?php echo $this->getUri()->create(array($op->radix->shortname, 'thread', $num)); ?> " class="btnr parent"><?php echo _i('View'); ?> </a><a href="<?php echo $this->getUri()->create(array($op->radix->shortname, $controller_method, $num)) . '#reply'; ?> " class="btnr parent"><?php echo _i('Reply'); ?> </a><?php echo isset($post['omitted']) && $post['omitted'] > 50 ? '<a href="' . $this->getUri()->create($op->radix->shortname . '/last/50/' . $num) . '" class="btnr parent">' . _i('Last 50') . '</a>' : ''; echo $op->radix->archive ? '<a href="//boards.4chan.org/' . $op->radix->shortname . '/thread/' . $num . '" class="btnr parent">' . _i('Original') . '</a>' : ''; ?> <a href="#" class="btnr parent" data-post="<?php echo $op->doc_id; ?> " data-post-id="<?php echo $num; ?> " data-board="<?php echo htmlspecialchars($op->radix->shortname); ?> " data-controls-modal="post_tools_modal" data-backdrop="true" data-keyboard="true" data-function="report"><?php echo _i('Report'); ?> </a><?php if ($this->getAuth()->hasAccess('maccess.mod') || !$op->radix->archive) { ?> <a href="#" class="btnr parent" data-post="<?php echo $op->doc_id; ?> " data-post-id="<?php echo $num; ?> " data-board="<?php echo htmlspecialchars($op->radix->shortname); ?> " data-controls-modal="post_tools_modal" data-backdrop="true" data-keyboard="true" data-function="delete"><?php echo _i('Delete'); ?> </a><?php } ?> </span> <div class="backlink_list"<?php echo $op->getBacklinks() ? ' style="display:block"' : ''; ?> > <?php echo _i('Quoted By:'); ?> <span class="post_backlink" data-post="<?php echo $num; ?> "><?php echo $op->getBacklinks() ? implode(' ', $op->getBacklinks()) : ''; ?> </span> </div> <?php if ($this->getAuth()->hasAccess('maccess.mod')) { ?> <div class="btn-group" style="clear:both; padding:5px 0 0 0;"> <button class="btn btn-mini" data-function="activateModeration"><?php echo _i('Mod'); if ($op->poster_ip) { echo ' ' . Inet::dtop($op->poster_ip); } ?> </button> </div> <div class="btn-group post_mod_controls" style="clear:both; padding:5px 0 0 0;"> <button class="btn btn-mini" data-function="mod" data-board="<?php echo $op->radix->shortname; ?> " data-id="<?php echo $op->doc_id; ?> " data-action="delete_post"><?php echo _i('Delete Thread'); ?> </button> <?php if (!is_null($op_media)) { ?> <button class="btn btn-mini" data-function="mod" data-board="<?php echo $op->radix->shortname; ?> " data-id="<?php echo $op_media->media_id; ?> " data-doc-id="<?php echo $op->doc_id; ?> " data-action="delete_image"><?php echo _i('Delete Image'); ?> </button> <button class="btn btn-mini" data-function="mod" data-board="<?php echo $op->radix->shortname; ?> " data-id="<?php echo $op_media->media_id; ?> " data-doc-id="<?php echo $op->doc_id; ?> " data-action="ban_image_local"><?php echo _i('Ban Image'); ?> </button> <button class="btn btn-mini" data-function="mod" data-board="<?php echo $op->radix->shortname; ?> " data-id="<?php echo $op_media->media_id; ?> " data-doc-id="<?php echo $op->doc_id; ?> " data-action="ban_image_global"><?php echo _i('Ban Image Globally'); ?> </button> <?php } ?> <?php if ($op->poster_ip) { ?> <button class="btn btn-mini" data-function="ban" data-controls-modal="post_tools_modal" data-backdrop="true" data-keyboard="true" data-board="<?php echo $op->radix->shortname; ?> " data-ip="<?php echo Inet::dtop($op->poster_ip); ?> " data-action="ban_user"><?php echo _i('Ban IP:') . ' ' . Inet::dtop($op->poster_ip); ?> </button> <button class="btn btn-mini" data-function="searchUser" data-board="<?php echo $op->radix->shortname; ?> " data-board-url="<?php echo $this->getUri()->create(array($op->radix->shortname)); ?> " data-id="<?php echo $op->doc_id; ?> " data-poster-ip="<?php echo Inet::dtop($op->poster_ip); ?> "><?php echo _i('Search IP'); ?> </button> <?php if ($this->getPreferences()->get('foolslide.sphinx.global')) { ?> <button class="btn btn-mini" data-function="searchUserGlobal" data-board="<?php echo $op->radix->shortname; ?> " data-board-url="<?php echo $this->getUri()->create(array($op->radix->shortname)); ?> " data-id="<?php echo $op->doc_id; ?> " data-poster-ip="<?php echo Inet::dtop($op->poster_ip); ?> "><?php echo _i('Search IP Globally'); ?> </button> <?php } ?> <?php } ?> </div> <?php } ?> </div> </header> <div class="text<?php if (preg_match('/[\\x{4E00}-\\x{9FBF}\\x{3040}-\\x{309F}\\x{30A0}-\\x{30FF}]/u', $op->getCommentProcessed())) { echo ' shift-jis'; } ?> "> <?php echo $op->getCommentProcessed(); ?> </div> <div class="thread_tools_bottom"> <?php if (isset($post['omitted']) && $post['omitted'] > 0) { ?> <span class="omitted"> <a style="display:inline-block" href="<?php echo $this->getUri()->create(array($op->radix->shortname, $controller_method, $op->thread_num)); ?> " data-function="expandThread" data-thread-num="<?php echo $op->thread_num; ?> "><i class="icon icon-resize-full"></i></a> <span class="omitted_text"> <span class="omitted_posts"><?php echo $post['omitted']; ?> </span> <?php echo _n('post', 'posts', $post['omitted']); ?> <?php if (isset($post['images_omitted']) && $post['images_omitted'] > 0) { ?> <?php echo _i('and'); ?> <span class="omitted_images"><?php echo $post['images_omitted']; ?> </span> <?php echo _n('image', 'images', $post['images_omitted']); ?> <?php } ?> <?php echo _n('omitted', 'omitted', $post['omitted'] + $post['images_omitted']); ?> </span> <?php } ?> </div> <?php if ($op->getReports()) { ?> <?php foreach ($op->getReports() as $report) { ?> <div class="report_reason"><?php echo '<strong>' . _i('Reported Reason:') . '</strong> ' . $report->getReasonProcessed(); ?> <br/> <div class="ip_reporter"> <strong><?php echo _i('Info:'); ?> </strong> <?php echo Inet::dtop($report->ip_reporter); ?> , <?php echo _i('Type:'); ?> <?php echo $report->media_id !== null ? _i('media') : _i('post'); ?> , <?php echo _i('Time:'); ?> <?php echo gmdate('D M d H:i:s Y', $report->created); ?> <button class="btn btn-mini" data-function="mod" data-id="<?php echo $report->id; ?> " data-board="<?php echo htmlspecialchars($op->radix->shortname); ?> " data-action="delete_report"><?php echo _i('Delete Report'); ?> </button> </div> </div> <?php } ?> <?php } ?> <?php } elseif (isset($post['posts'])) { ?> <article class="clearfix thread"> <?php \Foolz\Plugin\Hook::forge('foolslide.themes.default_after_headless_open')->setObject($this)->setParam('board', array(isset($radix) ? $radix : null))->execute(); ?> <?php } ?> <aside class="posts"> <?php if (isset($post['posts'])) { $post_counter = 0; $image_counter = 0; $board_comment_view = $this->getBuilder()->createPartial('post', 'board_comment'); // reusable Comment object not to create one every loop $comment = new Comment($this->getContext()); $comment->setControllerMethod($controller_method); $media_obj = new Media($this->getContext()); $search = array('/\\>[^\\S ]+/s', '/[^\\S ]+\\</s', '/(\\s)+/s'); $replace = array('>', '<', '\\1'); foreach ($post['posts'] as $p) { /** @var CommentBulk $p */ $post_counter++; if ($p->media !== null) { $image_counter++; } if ($image_counter == 150) { $modifiers['lazyload'] = true; } $comment->setBulk($p); // set the $media to null and leave the Media object in existence if ($p->media !== null) { $media_obj->setBulk($p); $media = $media_obj; } else { $media = null; } $board_comment_view->getParamManager()->setParams(['p' => $comment, 'p_media' => $media, 'modifiers' => $this->getBuilderParamManager()->getParam('modifiers', false), 'post_counter' => $post_counter, 'image_counter' => $image_counter]); // refreshes the string $board_comment_view->doBuild(); echo preg_replace($search, $replace, $board_comment_view->build()); // remove extra strings from the objects $board_comment_view->clearBuilt(); $p->comment->clean(); if ($p->media !== null) { $p->media->clean(); } $this->flush(); } } ?> </aside> <?php if ($thread_id !== 0) { ?> <div class="js_hook_realtimethread"></div> <?php echo $this->getBuilder()->isPartial('tools_reply_box') ? $this->getBuilder()->getPartial('tools_reply_box')->build() : ''; ?> <?php } ?> <?php if (isset($post['op']) || isset($post['posts'])) { ?> </article> <?php } ?> <?php } ?> <article class="clearfix thread backlink_container"> <div id="backlink" style="position: absolute; top: 0; left: 0; z-index: 5;"></div> </article> <?php }
/** * Inserts the media uploaded (static::forgeFromUpload()) * * @param string $microtime The time in microseconds to use for the local filename * @param boolean $is_op True if the thumbnail sizes should be for OP, false if they should be for a reply * * @return \Foolz\Foolslide\Model\Media The current object updated with the insert data * @throws MediaInsertInvalidFormatException In case the media is not a valid format * @throws MediaInsertDomainException In case the media uploaded is in example too small or validations don't pass * @throws MediaInsertRepostException In case the media has been reposted too recently according to control panel settings * @throws MediaThumbnailCreationException If the thumbnail fails to generate */ public function p_insert($microtime, $is_op) { $file = $this->temp_file; $this->op = $is_op; $data = \Foolz\Plugin\Hook::forge('Foolz\\Foolslide\\Model\\Media::insert.result.media_data')->setParams(['dimensions' => getimagesize($file->getPathname()), 'file' => $file, 'name' => $file->getClientOriginalName(), 'path' => $file->getPathname(), 'hash' => base64_encode(pack("H*", md5(file_get_contents($file->getPathname())))), 'size' => $file->getClientSize(), 'time' => $microtime, 'media_orig' => $microtime . '.' . strtolower($file->getClientOriginalExtension()), 'preview_orig' => $microtime . 's.' . strtolower($file->getClientOriginalExtension())])->execute()->getParams(); if (!($getimagesize = $data['dimensions'])) { throw new MediaInsertInvalidFormatException(_i('The file you uploaded is not allowed.')); } // if width and height are lower than 25 reject the image if ($getimagesize[0] < 25 || $getimagesize[1] < 25) { throw new MediaInsertDomainException(_i('The file you uploaded is too small.')); } if ($getimagesize[0] > $this->radix->getValue('max_image_size_width') || $getimagesize[1] > $this->radix->getValue('max_image_size_height')) { throw new MediaInsertDomainException(_i('The dimensions of the file you uploaded is too large.')); } $this->media->media_w = $getimagesize[0]; $this->media->media_h = $getimagesize[1]; $this->media->media_filename = $data['name']; $this->media->media_hash = $data['hash']; $this->media->media_size = $data['size']; $this->media->media_orig = $data['media_orig']; $this->media->preview_orig = $data['preview_orig']; $do_thumb = true; $do_full = true; try { $duplicate = CommentBulk::forge($this->radix, null, $this->media_factory->getByMediaHash($this->radix, $this->media->media_hash)); $duplicate = new Media($this->getContext(), $duplicate); // we want the current media to work with the same filenames as previously stored $this->media->media_id = $duplicate->media->media_id; $this->media->media = $duplicate->media->media; $this->media->media_orig = $duplicate->media->media; $this->media->preview_op = $duplicate->media->preview_op; $this->media->preview_reply = $duplicate->media->preview_reply; if (!$this->getAuth()->hasAccess('comment.limitless_comment') && $this->radix->getValue('min_image_repost_time')) { // if it's -1 it means that image reposting is disabled, so this image shouldn't pass if ($this->radix->getValue('min_image_repost_time') == -1) { throw new MediaInsertRepostException(_i('This image has already been posted once. This board doesn\'t allow image reposting.')); } // we don't have to worry about archives with weird timestamps, we can't post images there $duplicate_entry = $this->dc->qb()->select('COUNT(*) as count, MAX(timestamp) as max_timestamp')->from($this->radix->getTable(), 'r')->where('media_id = :media_id')->andWhere('timestamp > :timestamp')->setParameter('media_id', $duplicate->media->media_id)->setParameter('timestamp', time() - $this->radix->getValue('min_image_repost_time'))->setMaxResults(1)->execute()->fetch(); if ($duplicate_entry['count']) { $datetime = new \DateTime(date('Y-m-d H:i:s', $duplicate_entry['max_timestamp'] + $this->radix->getValue('min_image_repost_time'))); $remain = $datetime->diff(new \DateTime()); throw new MediaInsertRepostException(_i('This image has been posted recently. You will be able to post it again in %s.', ($remain->d > 0 ? $remain->d . ' ' . _i('day(s)') : '') . ' ' . ($remain->h > 0 ? $remain->h . ' ' . _i('hour(s)') : '') . ' ' . ($remain->i > 0 ? $remain->i . ' ' . _i('minute(s)') : '') . ' ' . ($remain->s > 0 ? $remain->s . ' ' . _i('second(s)') : ''))); } } // if we're here, we got the media $duplicate_dir = $duplicate->getDir(); if ($duplicate_dir !== null && file_exists($duplicate_dir)) { $do_full = false; } $duplicate->op = $is_op; $duplicate_dir_thumb = $duplicate->getDir(true, true); if ($duplicate_dir_thumb !== null && file_exists($duplicate_dir_thumb)) { $duplicate_dir_thumb_size = getimagesize($duplicate_dir_thumb); $this->media->preview_w = $duplicate_dir_thumb_size[0]; $this->media->preview_h = $duplicate_dir_thumb_size[1]; $do_thumb = false; } } catch (MediaNotFoundException $e) { } if ($do_thumb) { $thumb_width = $this->radix->getValue('thumbnail_reply_width'); $thumb_height = $this->radix->getValue('thumbnail_reply_height'); if ($is_op) { $thumb_width = $this->radix->getValue('thumbnail_op_width'); $thumb_height = $this->radix->getValue('thumbnail_op_height'); } if (!file_exists($this->pathFromFilename(true, $is_op))) { mkdir($this->pathFromFilename(true, $is_op), 0777, true); } $return = \Foolz\Plugin\Hook::forge('Foolz\\Foolslide\\Model\\Media::insert.result.create_thumbnail')->setObject($this)->setParams(['thumb_width' => $thumb_width, 'thumb_height' => $thumb_height, 'exec' => str_replace(' ', '\\ ', $this->preferences->get('foolframe.imagick.convert_path')), 'is_op' => $is_op, 'media' => $file, 'thumb' => $this->pathFromFilename(true, $is_op, true)])->execute()->get(); if ($return instanceof \Foolz\Plugin\Void) { if ($this->radix->getValue('enable_animated_gif_thumbs') && strtolower($file->getClientOriginalExtension()) === 'gif') { exec(str_replace(' ', '\\ ', $this->preferences->get('foolframe.imagick.convert_path')) . " " . $data['path'] . " -coalesce -treedepth 4 -colors 256 -quality 80 -background none " . "-resize \"" . $thumb_width . "x" . $thumb_height . ">\" " . $this->pathFromFilename(true, $is_op, true)); } else { exec(str_replace(' ', '\\ ', $this->preferences->get('foolframe.imagick.convert_path')) . " " . $data['path'] . "[0] -quality 80 -background none " . "-resize \"" . $thumb_width . "x" . $thumb_height . ">\" " . $this->pathFromFilename(true, $is_op, true)); } } if (!file_exists($this->pathFromFilename(true, $is_op, true))) { throw new MediaThumbnailCreationException(_i('The thumbnail failed to generate.')); } $thumb_getimagesize = getimagesize($this->pathFromFilename(true, $is_op, true)); $this->media->preview_w = $thumb_getimagesize[0]; $this->media->preview_h = $thumb_getimagesize[1]; if ($do_full) { if (!file_exists($this->pathFromFilename())) { mkdir($this->pathFromFilename(), 0777, true); } copy($data['path'], $this->pathFromFilename(false, false, true)); } } if (function_exists('exif_read_data') && in_array(strtolower($file->getClientOriginalExtension()), ['jpg', 'jpeg', 'tiff'])) { $exif_data = null; getimagesize($data['path'], $exif_data); if (!isset($exif_data['APP1']) || strpos($exif_data['APP1'], 'Exif') === 0) { $exif = exif_read_data($data['path']); if ($exif !== false) { $this->media->exif = $exif; } } } if (!$this->media->media_id) { $this->dc->getConnection()->insert($this->radix->getTable('_images'), ['media_hash' => $this->media->media_hash, 'media' => $this->media->media_orig, 'preview_op' => $this->op ? $this->media->preview_orig : null, 'preview_reply' => !$this->op ? $this->media->preview_orig : null, 'total' => 1, 'banned' => 0]); $this->media->media_id = $this->dc->getConnection()->lastInsertId($this->radix->getTable('_images_media_id_seq')); } else { $media_sql = $this->dc->qb()->select('COUNT(*)')->from($this->radix->getTable(), 't')->where('media_id = :media_id')->setParameter(':media_id', $this->media->media_id)->getSQL(); $query = $this->dc->qb()->update($this->radix->getTable('_images')); if ($this->media === null) { $query->set('media', ':media_orig')->setParameter(':media_orig', $this->media->preview_orig); } if ($this->op && $this->media->preview_op === null) { $query->set('preview_op', ':preview_orig')->setParameter(':preview_orig', $this->media->preview_orig); } if (!$this->op && $this->media->preview_reply === null) { $query->set('preview_reply', ':preview_orig')->setParameter(':preview_orig', $this->media->preview_orig); } $query->set('total', '(' . $media_sql . ')'); $query->where('media_id = :media_id')->setParameter(':media_id', $this->media->media_id)->execute(); } return $this; }
/** * Delete the post and eventually the entire thread if it's OP * Also deletes the images when it's the only post with that image * * @param null $password * @param bool $force * @param bool $thread * @throws CommentSendingDatabaseException * @throws CommentDeleteWrongPassException * @return array|bool */ protected function p_delete($password = null, $force = false, $thread = false) { if (!$this->getAuth()->hasAccess('comment.passwordless_deletion') && $force !== true) { if (!password_verify($password, $this->comment->getDelpass())) { throw new CommentDeleteWrongPassException(_i('You did not provide the correct deletion password.')); } } try { $this->dc->getConnection()->beginTransaction(); // check that the post isn't already in deleted $has_deleted = $this->dc->qb()->select('COUNT(*) as found')->from($this->radix->getTable('_deleted'), 'd')->where('doc_id = :doc_id')->setParameter(':doc_id', $this->comment->doc_id)->execute()->fetch(); if (!$has_deleted['found']) { // throw into _deleted table $this->dc->getConnection()->executeUpdate('INSERT INTO ' . $this->radix->getTable('_deleted') . ' ' . $this->dc->qb()->select('*')->from($this->radix->getTable(), 't')->where('doc_id = ' . $this->dc->getConnection()->quote($this->comment->doc_id))->getSQL()); } // delete post $this->dc->qb()->delete($this->radix->getTable())->where('doc_id = :doc_id')->setParameter(':doc_id', $this->comment->doc_id)->execute(); // purge reports $this->dc->qb()->delete($this->dc->p('reports'))->where('board_id = :board_id')->andWhere('doc_id = :doc_id')->setParameter(':board_id', $this->radix->id)->setParameter(':doc_id', $this->comment->doc_id)->execute(); // clear cache $this->radix_coll->clearCache(); // remove image file if (isset($this->media)) { $media_sql = $this->dc->qb()->select('COUNT(*)')->from($this->radix->getTable(), 't')->where('media_id = :media_id')->setParameter(':media_id', $this->media->media_id)->getSQL(); $this->dc->qb()->update($this->radix->getTable('_images'))->set('total', '(' . $media_sql . ')')->where('media_id = :media_id')->setParameter(':media_id', $this->media->media_id)->execute(); $has_image = $this->dc->qb()->select('total')->from($this->radix->getTable('_images'), 'ti')->where('media_id = :media_id')->setParameter(':media_id', $this->media->media_id)->execute()->fetch(); if (!$has_image || !$has_image['total']) { $media = new Media($this->getContext(), $this->bulk); $media->delete(); } } // if this is OP, delete replies too if ($this->comment->op) { // delete thread data $this->dc->qb()->delete($this->radix->getTable('_threads'))->where('thread_num = :thread_num')->setParameter(':thread_num', $this->comment->thread_num)->execute(); // process each comment $comments = $this->dc->qb()->select('doc_id')->from($this->radix->getTable(), 'b')->where('thread_num = :thread_num')->setParameter(':thread_num', $this->comment->thread_num)->execute()->fetchAll(); foreach ($comments as $comment) { $post = Board::forge($this->getContext())->getPost()->setOptions('doc_id', $comment['doc_id'])->setRadix($this->radix)->getComments(); $post = current($post); $post = new Comment($this->getContext(), $post); $post->delete(null, true, true); } } else { // if this is not triggered by a thread deletion, update the thread table if ($thread === false && !$this->radix->archive) { $time_last = ' ( COALESCE(GREATEST( time_op, ( SELECT MAX(timestamp) FROM ' . $this->radix->getTable() . ' xr WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' AND subnum = 0 ) ), time_op) )'; $time_bump = ' ( COALESCE(GREATEST( time_op, ( SELECT MAX(timestamp) FROM ' . $this->radix->getTable() . ' xr WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' AND subnum = 0 AND (email <> \'sage\' OR email IS NULL) ) ), time_op) )'; $time_ghost = ' ( SELECT MAX(timestamp) FROM ' . $this->radix->getTable() . ' xr WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' AND subnum <> 0 )'; $time_ghost_bump = ' ( SELECT MAX(timestamp) FROM ' . $this->radix->getTable() . ' xr WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' AND subnum <> 0 AND (email <> \'sage\' OR email IS NULL) )'; // update thread information $this->dc->qb()->update($this->radix->getTable('_threads'))->set('time_last', $time_last)->set('time_bump', $time_bump)->set('time_ghost', $time_ghost)->set('time_ghost_bump', $time_ghost_bump)->set('time_last_modified', ':time')->set('nreplies', 'nreplies - 1')->set('nimages', $this->media === null ? 'nimages' : 'nimages - 1')->where('thread_num = :thread_num')->setParameter(':time', $this->getRadixTime())->setParameter(':thread_num', $this->comment->thread_num)->execute(); } } $this->dc->getConnection()->commit(); // clean up some caches Cache::item('foolslide.model.board.getThreadComments.thread.' . md5(serialize([$this->radix->shortname, $this->comment->thread_num])))->delete(); // clean up the 10 first pages of index and gallery that are cached for ($i = 1; $i <= 10; $i++) { Cache::item('foolslide.model.board.getLatestComments.query.' . $this->radix->shortname . '.by_post.' . $i)->delete(); Cache::item('foolslide.model.board.getLatestComments.query.' . $this->radix->shortname . '.by_thread.' . $i)->delete(); Cache::item('foolslide.model.board.getThreadsComments.query.' . $this->radix->shortname . '.' . $i)->delete(); } } catch (\Doctrine\DBAL\DBALException $e) { $this->logger->error('\\Foolz\\Foolslide\\Model\\CommentInsert: ' . $e->getMessage()); $this->dc->getConnection()->rollBack(); throw new CommentSendingDatabaseException(_i('Something went wrong when deleting the post in the database. Try again.')); } return $this; }
public function submit($data, $media) { // some beginners' validation, while through validation will happen in the Comment model $validator = new Validator(); $validator->add('thread_num', _i('Thread Number'), [new Assert\NotBlank()])->add('name', _i('Name'), [new Assert\Length(['max' => 64])])->add('email', _i('Email'), [new Assert\Length(['max' => 64])])->add('title', _i('Title'), [new Assert\Length(['max' => 64])])->add('delpass', _i('Deletion pass'), [new Assert\Length(['min' => 3, 'max' => 32])]); // no empty posts without images if ($media === null) { $validator->add('comment', _i('Comment'), [new Assert\NotBlank(), new Assert\Length(['min' => 3])]); } // this is for redirecting, not for the database $limit = false; if (isset($data['last_limit'])) { $limit = intval($data['last_limit']); unset($data['last_limit']); } $validator->validate($data); if (!$validator->getViolations()->count()) { try { $data['poster_ip'] = Inet::ptod($this->getRequest()->getClientIp()); $bulk = new CommentBulk(); $bulk->import($data, $this->radix); $comment = new CommentInsert($this->getContext(), $bulk); $comment->insert($media, $data); } catch (\Foolz\Foolslide\Model\CommentSendingRequestCaptchaException $e) { if ($this->getRequest()->isXmlHttpRequest()) { return $this->response->setData(['captcha' => true]); } else { return $this->error(_i('Your message looked like spam. Make sure you have JavaScript enabled to display the reCAPTCHA to submit the comment.')); } } catch (\Foolz\Foolslide\Model\CommentSendingException $e) { if ($this->getRequest()->isXmlHttpRequest()) { return $this->response->setData(['error' => $e->getMessage()]); } else { return $this->error($e->getMessage()); } } } else { if ($this->getRequest()->isXmlHttpRequest()) { return $this->response->setData(['error' => $validator->getViolations()->getText()]); } else { return $this->error($validator->getViolations()->getHtml()); } } if ($this->request->isXmlHttpRequest()) { $latest_doc_id = $this->getPost('latest_doc_id'); if ($latest_doc_id && ctype_digit((string) $latest_doc_id)) { try { $board = Board::forge($this->getContext())->getThread($comment->comment->thread_num)->setRadix($this->radix)->setOptions(['type' => 'from_doc_id', 'latest_doc_id' => $latest_doc_id]); $comments = $board->getComments(); } catch (\Foolz\Foolslide\Model\BoardThreadNotFoundException $e) { return $this->error(_i('Thread not found.')); } catch (\Foolz\Foolslide\Model\BoardException $e) { return $this->error(_i('Unknown error.')); } $comment_obj = new Comment($this->getContext()); $comment_obj->setControllerMethod($limit ? 'last/' . $limit : 'thread'); $media_obj = new Media($this->getContext()); $m = null; foreach ($board->getCommentsUnsorted() as $bulk) { $comment_obj->setBulk($bulk, $this->radix); if ($bulk->media) { $media_obj->setBulk($bulk, $this->radix); $m = $media_obj; } else { $m = null; } if ($this->builder) { $this->param_manager->setParam('controller_method', $limit ? 'last/' . $limit : 'thread'); $partial = $this->builder->createPartial('board_comment', 'board_comment'); $partial->getParamManager()->setParam('p', $comment_obj)->setParam('p_media', $m); $bulk->comment->formatted = $partial->build(); $partial->clearBuilt(); } } $this->response->setData(['success' => _i('Message sent.')] + $comments); } else { if ($this->builder) { $this->param_manager->setParam('controller_method', $limit ? 'last/' . $limit : 'thread'); $partial = $this->builder->createPartial('board_comment', 'board_comment'); $partial->getParamManager()->setParam('p', new Comment($this->getContext(), $comment->bulk))->setParam('p_media', new Media($this->getContext(), $comment->bulk)); $bulk->comment->formatted = $partial->build(); $partial->clearBuilt(); } $this->response->setData(['success' => _i('Message sent.'), 'thread_num' => $comment->comment->thread_num, $comment->comment->thread_num => ['posts' => [$comment->bulk]]]); } } else { $this->builder->createLayout('redirect')->getParamManager()->setParam('url', $this->uri->create([$this->radix->shortname, !$limit ? 'thread' : 'last/' . $limit, $comment->comment->thread_num]) . '#' . $comment->comment->num); $this->builder->getProps()->addTitle(_i('Redirecting')); $this->response->setContent($this->builder->build()); } return $this->response; }