10

I am creating a proof of concept for a client in Craft where the client's customers can manage projects from the frontend of a Craft install.

I have a frontend form working correctly for creating a new project and uploading files attached to it... however the missing piece to the puzzle for full management is being able to delete a project entry and also being able to manage the media attached to the project entry.

Is it possible to let a frontend user manage assets, in particular deleting assets from their username folder?

5
  • 1
    Hey Alan, I just removed your two additional questions, as they each warrant their own posts. Commented Aug 19, 2014 at 20:55
  • 1
    Thanks @BrandonKelly so much for your thorough answer and the edits. I hope I am not spamming the SE site too much by breaking the other questions out into two new questions: How do you delete entries from a frontend form? and Is it possible to use the assets modal on the front end?. Commented Aug 20, 2014 at 4:10
  • @AlanMiller Not spamming at all, glad you split these into separate questions! As individual questions, you'll be able to get better, more detailed responses. Plus, it's way more useful to any future visitors who may only share one of your questions. Splitting them up is definitely the right move!
    – Lindsey D
    Commented Aug 20, 2014 at 5:15
  • @brandon is their a way to delete an asset in this manner in Craft 3?
    – kinoli
    Commented Jan 28, 2019 at 6:23
  • @kinoli just updated my answer for Craft 3. Commented Jan 29, 2019 at 11:44

1 Answer 1

9

You can create a POST request via Ajax to the assets/deleteFile (Craft 2) / assets/delete-asset (Craft 3) controller action:

Craft 2:

$('#delete-file').click(function() {
    var data = {
        fileId: 100
    };

    $.post('/actions/assets/deleteFile', data, function(response) {
        // ...
    });
});

Craft 3:

$('#delete-file').click(function() {
    var data = {
        assetId: 100
    };

    $.post('/actions/assets/delete-asset', data, function(response) {
        // ...
    });
});

That controller action will require that the user is logged-in with a user account that has the “Remove files” permission on the asset source that the file belongs to. Granting that permission to a user will give them the ability to delete any file within that asset source though - not just their own. So this is probably not a good idea.

A better way of doing this would be to write a module that provides its own controller that does its own permission checking and handles the file deletion. It would look something like this:

Craft 2:

class MyController extends BaseController
{
    public function actionDeleteFile()
    {
        $this->requireAjaxRequest();

        $fileId = craft()->request->getRequiredPost('fileId');
        $file = craft()->assets->getFileById($fileId);

        if (!$file)
        {
            throw new Exception('Could not find a file with the ID '.$fileId);
        }

        // Make sure that it's within a folder with the same name as the user's username
        if ($file->getFolder()->name != craft()->userSession->getUser()->username)
        {
            throw new HttpException(403);
        }

        $response = craft()->assets->deleteFiles($fileIds);
        $this->returnJson($response->getResponseData());
    }
}

Craft 3:

use Craft;
use craft\web\Controller;
use yii\web\BadRequestHttpException;
use yii\web\ForbiddenHttpException;
use yii\web\Response;

class MyController extends Controller
{
    public function actionDeleteFile(): Response
    {
        $this->requireAcceptsJson();

        $assetId = Craft::$app->request->getRequiredBodyParam('fileId');
        $asset = Craft::$app->assets->getAssetById($assetId);

        if (!$asset) {
            throw new BadRequestHttpException('Invalid asset ID: ' . $assetId);
        }

        // Make sure that it's within a folder with the same name as the user's username
        if ($asset->getFolder()->name != Craft::$app->user->getIdentity()->username) {
            throw new ForbiddenHttpException('Not permitted to delete asset ' . $assetId);
        }

        $success = Craft::$app->elements->deleteElement($asset);
        return $this->asJson([
            'success' => $success,
        ]);
    }
}

With that in place, you’d just need to update your JavaScript to point to your deleteFile/delete-file action instead of Craft’s.

5
  • So, how do I get the fileId to pass into the controller? I have tried {{entry.myFileHandle.id}} and a bunch of other stuff that is not working for me. Commented Sep 18, 2014 at 22:27
  • 1
    @DavidAaronMcInnis entry.myAssetsFieldHandle.first().id would be one way. Commented Sep 18, 2014 at 22:28
  • Does the above post via Ajax method for deleting a file in Craft 3 still work? Been trying it but just getting error: Bad request yii\web\BadRequestHttpException: Unable to verify your data submission
    – mmc501
    Commented Dec 4, 2019 at 16:46
  • I tried using this code with Craft 4, but its not going well 😅 I created a module using the module generator, but I’m getting a 404 error for the action /actions/deleteFile/delete-file and invalid id undefined response. I don't understand how I could get a 404 and an error response from the code that it can't reach.
    – fdelaneau
    Commented Jan 9 at 11:06
  • 1
    @fdelaneau Actions should be kebab-case in Craft 3+ (Yii2). (“deleteFile/delete-file” was me demonstrating the two ways you might access it, depending on Craft 2 or 3+.) The entire module URI will be composed of your module ID, controller name (sans Controller) and action name (sans action). So for example, if the module ID is foo-bar and your controller is named BazController, and action method is actionDeleteFile(), the URI would be actions/foo-bar/baz/delete-file. Commented Jan 9 at 19:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.