Create file attachment from an extension

Discussion forum for Extension Writers regarding Extension Development.
MitjaStachowiak
Registered User
Posts: 39
Joined: Mon Dec 30, 2019 7:29 pm
Location: Reinheim, Germany

Create file attachment from an extension

Post by MitjaStachowiak »

Hello,
for my Email-On-Topic-Extension, I've written a bridge for Telegram-Channels (not yet in the release). A forum topic can bidirectionally synchronize with a telegram-channel, but such tings like images are ignored.

I would need to import files from telegram as attachments into phpBB with my extension. The bot-user used to bring telegram messages to bhpBB, does not login. The telegram-Webhook is verified by an extra-password and the extension writes the posts directly to the database without user login.

So which functions should I use to create an attachment? There should still be checks for allowed file extensions.
User avatar
ViolaF
I've Been Banned!
Posts: 1609
Joined: Tue Aug 14, 2012 11:52 pm

Re: Create file attachment from an extension

Post by ViolaF »

What a great Hack this would be :mrgreen:
Awaiting solution :geek:
rxu
Extensions Development Team
Posts: 3967
Joined: Wed Oct 25, 2006 12:46 pm
Location: Siberia, Russian Federation

Re: Create file attachment from an extension

Post by rxu »

MitjaStachowiak
Registered User
Posts: 39
Joined: Mon Dec 30, 2019 7:29 pm
Location: Reinheim, Germany

Re: Create file attachment from an extension

Post by MitjaStachowiak »

Okay, I tried the following code:

Code: Select all

$upload = $this->phpbb_container->get('files.upload');
$cache = $this->phpbb_container->get('cache');
$upload->set_disallowed_content([]);
$extensions = $cache->obtain_attach_extensions($forum_id);
$upload->set_allowed_extensions(array_keys($extensions['_allowed_']));
$file = $upload->handle_upload('files.types.remote', 'https://mitjastachowiak.de/images/Titelbild.jpg');
print_r($file->error);
This seems to load the file to the /tmp directory of the server. But not to the files-directory of phpBB and there is no entry in the attachments table.

I'm now searching for a way to convert the uploaded tmp file into an attachment linked with a post-id.

[Edit:] Okay, the function upload in /phpbb/attachment/upload.php contains relevant code lines to integrate uploaded files as attachments...
MitjaStachowiak
Registered User
Posts: 39
Joined: Mon Dec 30, 2019 7:29 pm
Location: Reinheim, Germany

Re: Create file attachment from an extension

Post by MitjaStachowiak »

I found a way:

Code: Select all

  /*
   addAttachmentToPost
   Downloads a file from a given $url to the local server and adds it as an attachment to the post with the given $postId. This post must exist.
   The attachment's $comment is optional and HTML-Code will not be escaped. You have to apply htmlspecialchars() on forign/user-input content given as comment!
   If $dispatchEvent is true, the core.modify_attachment_sql_ary_on_submit will be triggered to allow other extensions to work on the attachment.
   If $topicId, $forumId AND the poster's $userId (which is not always the current user's ID) of the given post are known, they can be given to save one SQL-query.
   $realFileName can be given; if empty, the file name is taken from the url.
   Returns an array holding the error information. If successfull, first array element is 'SUCCESS', second element the ID of the new attachment, and third element again the post ID.
  */
  private function addAttachmentToPost ($url, $postId, $comment='', $dispatchEvent=true, $topicId=0, $forumId=0, $userId=0, $realFileName='') {
    if (getType($url) != 'string' || getType($postId) != 'integer' || getType($comment) != 'string' || getType($dispatchEvent) != 'boolean' || getType($topicId) != 'integer' || getType($forumId) != 'integer' || getType($userId) != 'integer') throw 'Type Missmatch';
    if ($postId <= 0) throw 'Post ID cannot be zero!';
    // if not given, get missing IDs
    if ($topicId == 0 || $forumId == 0 || $userId == 0) {
      $idRow = $this->db->sql_fetchrow($this->db->sql_query('SELECT post_id, topic_id, forum_id, poster_id FROM '.$this->table_prefix.'posts WHERE post_id = '.$postId));
      if (!$idRow)
        return ['POST_NOT_EXISTS'];
      $topicId = intval($idRow['topic_id']);
      $forumId = intval($idRow['topic_id']);
      $userId = intval($idRow['poster_id']);
    }
    // check attachment count
    if (intval($this->config['max_attachments']) >= 0) {
      $cnt = $this->db->sql_fetchrow($this->db->sql_query('SELECT COUNT(*) as cnt FROM '.$this->table_prefix.'attachments WHERE post_msg_id = '.$postId))['cnt'];
      if ($cnt >= intval($this->config['max_attachments']))
        return ['MAX_ATTACHMENTS_PER_POST_EXCEEDED'];
    }
    // get required classes
    $upload = $this->phpbb_container->get('files.upload');
    $cache = $this->phpbb_container->get('cache');
    $attach = $this->phpbb_container->get('attachment.upload');
    // load file from remote location to local server
    $upload->set_disallowed_content([]);
    $extensions = $cache->obtain_attach_extensions($forumId);
    $extensionArr = array_keys($extensions['_allowed_']);
    $upload->set_allowed_extensions($extensionArr);
    $tempFile = $upload->handle_upload('files.types.remote', $url);
    if (count($tempFile->error) > 0) {
      $ext = '.';
      if (strrpos($url, '.') !== false) $ext = substr($url, strrpos($url, '.')+1);
      if ($tempFile->error[0] == 'URL_INVALID' && !in_array($ext, $extensionArr)) return ['FILE_EXTENSION_NOT_ALLOWED', 'EXTENSION=.'.htmlspecialchars($ext)];
      return $tempFile->error;
    }
    $tempFileData = [
      'realname' => (($realFileName == '')?$tempFile->get('realname'):htmlspecialchars($realFileName)),
      'size' => $tempFile->get('filesize'),
      'type' => $tempFile->get('mimetype'),
    ];
    // create attachment from temp file
    if (!function_exists('create_thumbnail'))
      require($this->phpbb_root_path.'includes/functions_posting.php');
    $attachmentFileData = $attach->upload('', $forumId, true, $tempFile->get('filename'), false, $tempFileData);
    if (!$attachmentFileData['post_attach'])
      return ['FILE_ATTACH_ERROR'];
    if (count($attachmentFileData['error']) > 0)
      return $attachmentFileData['error'];
    $sql_ary = array(
      'physical_filename'	=> $attachmentFileData['physical_filename'],
      'attach_comment'		=> $comment,
      'real_filename'		=> $attachmentFileData['real_filename'],
      'extension'			=> $attachmentFileData['extension'],
      'mimetype'			=> $attachmentFileData['mimetype'],
      'filesize'			=> $attachmentFileData['filesize'],
      'filetime'			=> $attachmentFileData['filetime'],
      'thumbnail'			=> $attachmentFileData['thumbnail'],
      'is_orphan'			=> 0,
      'in_message'			=> 0,
      'poster_id'			=> $userId,
      'post_msg_id'			=> $postId,
      'topic_id'			=> $topicId,
    );
    if ($dispatchEvent) {
      $dispatcher = $this->phpbb_container->get('dispatcher');
      $vars = array('sql_ary');
      extract($dispatcher->trigger_event('core.modify_attachment_sql_ary_on_submit', compact($vars)));
    }
    $this->db->sql_query('INSERT INTO '.$this->table_prefix.'attachments '.$this->db->sql_build_array('INSERT', $sql_ary));
    $newAttachmentID = intval($this->db->sql_nextid());
    if ($newAttachmentID == 0) return ['SQL_ATTACHMENT_INSERT_ERROR'];
    $this->db->sql_query('UPDATE '.$this->table_prefix.'posts SET post_attachment = 1 WHERE post_id = '.$postId);
    return ['SUCCESS', $newAttachmentID, $postId];
  }
This function requires:
  • $this->db to be \phpbb\db\driver\driver_interface - '@dbal.conn'
  • $this->config to be \phpbb\config\config - '@config'
  • $this->phpbb_container to be dependency injector - '@service_container'
  • $this->table_prefix to be a string representing the phpBB's table prefix including the first _
  • $this->phpbb_root_path the root path to import other phpBB files - '%core.root_path%'
Please note, that html-code given as $comment will not be escaped. For some applications, it is helpful to keep some HTML-codes for file comments - but never use the quote-character ", because the comment can be the alt-attribute for images. But you can hide the comment by putting <!-- --> around it. This way you ~can~ store i.e. special file ids for each attachment.
If the database is not configured to store 4-byte-unicode-chars, you will need to perform utf8_encode_ucr on $comment and $realFileName.

I should be able to synchronize file extensions from telegram with the forum, soon.
Last edited by MitjaStachowiak on Wed Dec 09, 2020 10:36 pm, edited 4 times in total.
MitjaStachowiak
Registered User
Posts: 39
Joined: Mon Dec 30, 2019 7:29 pm
Location: Reinheim, Germany

Re: Create file attachment from an extension

Post by MitjaStachowiak »

Bug fixed: Not in every situation, the function create_thumbnail (required by upload) is available but needs to be loaded in advance :roll:

Return to “Extension Writers Discussion”