public function get_index() { if (!$this->check_board()) { return $this->response->setData(['error' => _i('No board selected.')])->setStatusCode(422); } $page = $this->getQuery('page'); if (!$page) { return $this->response->setData(['error' => _i('The "page" parameter is missing.')])->setStatusCode(422); } if (!ctype_digit((string) $page)) { return $this->response->setData(['error' => _i('The value for "page" is invalid.')])->setStatusCode(422); } $page = intval($page); try { $options = ['per_page' => $this->radix->getValue('threads_per_page'), 'per_thread' => 5, 'order' => 'by_thread']; $board = Board::forge($this->getContext())->getLatest()->setRadix($this->radix)->setPage($page)->setOptions($options); foreach ($board->getCommentsUnsorted() as $comment) { $this->apify($comment); } $this->response->setData($board->getComments()); } catch (\Foolz\Foolfuuka\Model\BoardThreadNotFoundException $e) { return $this->response->setData(['error' => _i('Thread not found.')]); } catch (\Foolz\Foolfuuka\Model\BoardException $e) { return $this->response->setData(['error' => _i('Encountered an unknown error.')])->setStatusCode(500); } return $this->response; }
public function radix_redirect($filename = null) { $redirect = $this->uri->create([$this->radix->shortname]) . $filename; if ($this->radix->archive) { $redirect = ($this->radix->getValue('images_url') ?: '//images.4chan.org/' . $this->radix->shortname . '/src/') . $filename; } $this->builder->createLayout('redirect')->getParamManager()->setParam('url', $redirect); $this->builder->getProps()->addTitle(_i('Redirecting')); return $this->response->setContent($this->builder->build()); }
/** * Returns an array specifying the thread statuses. * Returned array keys: closed, dead, disable_image_upload * * @return array An associative array with boolean values * @throws BoardNotCompatibleMethodException If the specified fetching method is not "getThreadComments" * @throws BoardThreadNotFoundException If the thread was not found */ protected function p_getThreadStatus() { if ($this->method_fetching !== 'getThreadComments') { throw new BoardNotCompatibleMethodException(); } extract($this->options); $thread = $this->dc->qb()->select('*')->from($this->radix->getTable('_threads'), 't')->where('thread_num = :thread_num')->setParameter(':thread_num', $num)->execute()->fetch(); if (!$thread) { throw new BoardThreadNotFoundException(_i('The thread you were looking for does not exist.')); } // define variables to override $ghost_post_present = false; $last_modified = $thread['time_last_modified']; if ($thread['time_ghost'] !== null) { $ghost_post_present = true; } if ($this->radix->archive) { $timestamp = new \DateTime(date('Y-m-d H:i:s', $last_modified), new \DateTimeZone('America/New_York')); $timestamp->setTimezone(new \DateTimeZone('UTC')); $last_modified = strtotime($timestamp->format('Y-m-d H:i:s')); } $result = ['sticky' => (bool) $thread['sticky'], 'closed' => (bool) $thread['locked'], 'dead' => (bool) $this->radix->archive, 'ghost_exist' => $ghost_post_present, 'disable_image_upload' => (bool) $this->radix->archive, 'last_modified' => $last_modified]; if ($this->radix->getValue('thread_lifetime') > 0 && time() - $thread['time_last'] > $this->radix->getValue('thread_lifetime') || $ghost_post_present) { $result['dead'] = true; $result['disable_image_upload'] = true; } if ($thread['nreplies'] > $this->radix->getValue('max_posts_count')) { $result['dead'] = true; $result['disable_image_upload'] = true; } elseif ($thread['nimages'] >= $this->radix->getValue('max_images_count')) { $result['disable_image_upload'] = true; } if ($this->radix->getValue('disable_ghost') && $result['dead']) { $result['closed'] = true; } return $result; }
/** * 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\Foolfuuka\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\\Foolfuuka\\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\\Foolfuuka\\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; }
/** * 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\Foolfuuka\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\\Foolfuuka\\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 FoolFuuka 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; }