• Extensions
  • FriendsOfFlarum upload, the intelligent file attachment extension

orschiro

That seems quite odd, indeed.

Maybe try increasing your max file size to 8192 or higher and see if that solves the issue? That should trigger a different error, but it's worth a go.

    Failed to load resource: the server responded with a status of 500 ()???

    I am currently in the process of migrating an old SMF Forum to Flarum - with fof-upload enabled. Is there a way to upload old SMF-Attachments via API to Flarum in this migration process?

    I managed to solve this for User-Avatars (see below) but i am somewhat at a loss to solve this for attachments to a post in Flarum. As Flarum needs fof-upload to upload images to posts, i guessed it had to be done via the plugins upload URL /api/fof/upload. Looking at the network traffic from /api/fof/upload it seems, that the only thing sent with the POST-request is the image itself, but no User-ID, filename or such things. And at least the User-ID might be necessary to populate actor_id in the table fof_upload_files correctly.

    Maybe somebody can point me in the right direction...

    // Avatar Migration
    $avatar = array();
    $avatar = $smf->query('SELECT * FROM `smf_attachments` WHERE ID_MEMBER = '.$userId)->fetchAll();
    // Buggy Avatar Picture for SMF user with id 739
    if (count($avatar) > 0 && $userId != 739) {
        // echo $userId."\n";
        // var_dump($avatar);
        $avatarUrl = smf_url."index.php?action=dlattach;attach=".$avatar[0]['ID_ATTACH'].";type=avatar";
    
        if (file_put_contents('/tmp/avatarmigration', fopen($avatarUrl, 'r'))) {
            $response = $api->request(
                'POST',
                'users/' . $userId . '/avatar',
                [
                    'multipart' => [
                        [
                            'name' => 'avatar',
                            'contents' => fopen('/tmp/avatarmigration', 'r'),
                            'filename' => 'avatar-migrated-from-smf.png'
                        ]
                    ]
                ]
            );
            // var_dump(json_decode($response->getBody(), true));
        }
    }

    I guess i figured it out. This seems to work:

    if (file_put_contents('/tmp/attachmentmigration', fopen($attachmentUrl, 'r'))) {
        $response = $api->request(
            'POST',
            'fof/upload',
            [
                'multipart' => [
                    [
                        'name' => 'files[]',
                        'filename' => $attachment['filename'],
                        'contents' => fopen('/tmp/attachmentmigration', 'r'),
                    ]
                ]
            ]
        );
        $fofUploadResponse = json_decode($response->getBody(), true);
        // print_r($fofUploadResponse);
    
        // update actor_id / user_id for file-attachment in database
        $fla->query("UPDATE fof_upload_files SET actor_id = ".$post->fla_id." WHERE id = ".$fofUploadResponse['data'][0]['id']);
    
        // prepare BBCode to attach at end of Post
        $fileAttachmentBBCode = $fileAttachmentBBCode."\n\n".$fofUploadResponse['data'][0]['attributes']['bbcode'];
    }

    All the migrations I have done in the past were "offline" migrations, where I directly populated the database with new records. You could create the uploaded file in the correct folder than directly insert the new line in the fof_upload_files table.

    Using the Flarum REST API is another solution, and your code seems to do the job.

    To provide an actor to the REST API you can either create a user access token and pass that token as a header during the request, or when using the internal API Client class like you do, you can use the ->withActor($actor) chain method https://github.com/flarum/core/blob/master/src/Api/Client.php#L61

    I don't think FoF Upload has any rate limiting so this method should work fine. It would have been more complicated to use the REST API if there were upload limits per user.

    There doesn't seem to be any risk here as long as you trust $post->fla_id, but beware of SQL injections when using ->query() method without SQL bindings. $fofUploadResponse['data'][0]['id'] can be trusted to always be a string if the server isn't compromised. You could build the same request using Laravel's query builder, or by using bindings with the values passed as a second parameter.

      Thanks for your feedback. As security should always be a concern, the migration-script will run one (final) time on the CLI and i trust the script and data so far.

      I'm also switching servers and the forums subdomain in the process. Therefore to download the attachments from the old server and insert it via API seems to be the "smoother" approach. I am currently testing with a complete dataset (336k posts with "only" 530 attachments) to see if there are some issues or rate limits.

      clarkwinkelmann To provide an actor to the REST API you can either create a user access token and pass that token as a header during the request, or when using the internal API Client class like you do, you can use the ->withActor($actor) chain method https://github.com/flarum/core/blob/master/src/Api/Client.php#L61

      Thanks for the hint. But i am not sure, if i understand it correctly? What do you mean with "internal API Client"? I use $api = new GuzzleHttp\Client([...]) in my migration-script to connect to the API of Flarum. Adding $response = $api->request(...)->withActor($actor) results in an error. Besides $actor needs to be a User-Object and i only have the ID of the Post-User when looping over the individual posts.

        wolfman oops, sorry. I had assumed your code was using Flarum\Api\Client, which is a class available to Flarum extensions to perform requests to REST endpoints without actually making any real HTTP request. If your script is not a Flarum extension, it won't have access to that feature.

        Actually, access tokens probably aren't a realistic approach here. But an API Key might be a solution because you can specify which actor you want while using a single global key. Maybe you are already using an API key in the part of your code that's not visible (Guzzle client constructor parameters)? In the Authorization header you can specify the user ID with ;userId=1 at the end when passing an API key.

        Unfortunately it's not well documented at the moment. Some info about access tokens and API keys can be found here flarum/docs44 I can give more details if needed.

        @clarkwinkelmann I am using a separate/standalone PHP-CLI-script for the migration, and yes, it is like you said, i am using an API Key with a User-ID to connect to the API.

        $api = new GuzzleHttp\Client([
            'base_uri' => 'https://example.com/api/',
            'headers'  => [
                'User-Agent'   => 'SMF-Flarum-Migrate',
                'Accept'       => 'application/json',
                'Content-Type' => 'application/json',
                'Authorization'=> 'Token XYZ; userId=1',
            ]
        ]);

        I though, that userId in the Auhtorization-Header had to match the corresponding field api_keys.user_id in the database? Using that approach, i have to create a new API-connection with different userId's for every file uploaded via API - and probably have API-Keys for every User with Files to upload. I guess i stick with my implementation for the moment and move forward to the next problem regarding the migration. But thanks again for your feedback. It's good to know that this might be an option.

        Edit: I just checked tha database and api_keys.user_id can be NULL. So i guess there is no need for different API-Keys for each user, which makes this a cleaner approach.

          wolfman Edit: I just checked tha database and api_keys.user_id can be NULL. So i guess there is no need for different API-Keys for each user, which makes this a cleaner approach.

          That's exactly it. If you leave the value null in the database, then you are allowed to provide any userId during usage.

          cloudclassml there is currently no option for this in the extension. We can consider it. Someone could also submit a PR.

          If you don't need to use one of the FoF Upload bbcodes (image preview, file download, etc.) but only use the markdown or [img] bbcode, then this could also be implemented as a separate extension that would replace all imgur links in a post rather than FoF Upload URLs prior to insertion in the post content.

            clarkwinkelmann I don't really understand what you mean, but I'm really looking forward to having imgur's reverse proxy settings, and I look forward to implementing it soon!

            eddiewebb Current code here, https://github.com/eddiewebb/flarum-gpx-preview
            i am not using this extension right now but used the codes for FOF Upload

            How can i use *gpx files? *xml is working but i can't let FOF Upload use *gpx Files
            ^application/gpx+xml => not working for nothing
            ^application\/.*xml => works only for xml
            ^application\/.*gpx => not working for gpx

              6 days later

              This may have been discussed already, but after installing the extension in my forum and working with it for a while, I have a few comments and wondering if some of those are there but I'm missing them:

              • Is it possible to have separate file size limits based on the type of file? Because I have a music forum and people attach audio files and I have increased the file limit but then it also applies to images and I don't want people to be able to upload huge images
              • Is it possible to have a quota per user, e.g. to limit the total upload size per user per day. Also per post? Per discussion? Maybe a group based permission
              • Is it possible to limit/disable the upload in private discussions
              • Is there an easy way to see stats, such as a sorted list of users by their total upload. Also separated by private discussions
              • Is it possible to see the stats of the biggest files, as well as last accessed, total number of downloads, etc. For instance I would like to be able to delete files that have been uploaded long ago and are not accessed anymore.
              • Is it possible to list files that are not referred to from any discussion/post? Also files that were never accessed?
              • Is it possible to see a list of all the uploaded files and the corresponding posts that link to the file?
              • Is it possible to delete the actual files?

              I see I can do SQL queries for many of those but it's tedious. Would be great if there was an interface for that. Possibly another extension?

                CyberGene Is it possible to have separate file size limits based on the type of file? Because I have a music forum and people attach audio files and I have increased the file limit but then it also applies to images and I don't want people to be able to upload huge images

                Not possible right now. Like the idea, this could be build.

                CyberGene Is it possible to have a quota per user, e.g. to limit the total upload size per user per day. Also per post? Per discussion? Maybe a group based permission

                Not possible right now. Like the idea, this could be build.

                CyberGene Is it possible to limit/disable the upload in private discussions

                Not possible right now. Like the idea, this could be build.

                CyberGene Is there an easy way to see stats, such as a sorted list of users by their total upload. Also separated by private discussions

                Not possible right now. Uploads are connected to users though, it could be build.

                CyberGene Is it possible to see the stats of the biggest files, as well as last accessed, total number of downloads, etc. For instance I would like to be able to delete files that have been uploaded long ago and are not accessed anymore.

                Not possible right now. I think should be possible, but it might be a bit resource intensive, so probably this should be moved to a background task. Could be build.

                CyberGene Is it possible to list files that are not referred to from any discussion/post? Also files that were never accessed?

                This was on the roadmap, it could be build.

                CyberGene Is it possible to see a list of all the uploaded files and the corresponding posts that link to the file?

                This was planned, it could be build.

                CyberGene Is it possible to delete the actual files?

                This was planned, it could be build.


                All the features you mention I like. But our time is limited and extensions inside the Friends of Flarum namespace are meant to be maintained. Active development on those are usually commissioned or based on need of people able to develop and then contributed.

                If you would like to commission any of these features, make sure to take a look at https://flarum.org/partners for recommended developer parties.

                  luceos Thank you for your answer, it's promising that you have those planned! Thanks for the great forum and all the efforts!

                  We are a small community of piano hobbyists, so we won't be able to commission those changes. I'm a backend engineer and since the DB schema seems intuitive, I thought I could create some scripts or SQL queries that can gather the stats, so it's no hurry.