KarlvonKarton Posted November 18, 2024 Share Posted November 18, 2024 I've tried to make a custom file upload module that uploads the files in a folder outside the webroot, but without succes. I tried different approaches, but not getting anywhere... Any ideas how to tackle this? approach 1: <?php namespace ProcessWire; /** * FieldtypeCustomImage * Custom fieldtype that stores images outside the web root. */ class FieldtypeOriginalFile extends FieldtypeFile { public static function getModuleInfo() { return [ 'title' => 'Original Image Field', 'version' => 1, 'summary' => 'An original image field that stores images outside the web root.', 'requires' => ['FieldtypeFile', 'InputfieldOriginalFile'], 'icon' => 'image', ]; } /** * Initialize the module and hook into Pages::saved to move files after the page is saved. */ public function init() { parent::init(); $this->addHookAfter('FieldtypeFile::customizeFilePath', $this, 'customizeFilePath'); $this->addHookAfter('FieldtypeFile::customizeFileUrl', $this, 'customizeFileUrl'); } /** * Customize the file path to store files in a folder named after the page ID. */ public function customizeFilePath(HookEvent $event) { $page = $event->argumentsByName('page'); $field = $event->argumentsByName('field'); $filename = $event->argumentsByName('file'); if ($field->name !== 'originalphoto') return; // Define the base custom storage path $baseCustomPath = '/home/portila/originalphotos/'; // Create a folder named after the page ID $customPath = $baseCustomPath . $page->id . '/'; // Ensure the directory exists if (!is_dir($customPath)) { mkdir($customPath, 0755, true); } // Return the full path to the file $event->return = $customPath . basename($filename); } /** * Customize the URL to serve files from a custom handler. */ public function customizeFileUrl(HookEvent $event) { $page = $event->argumentsByName('page'); $field = $event->argumentsByName('field'); $filename = $event->argumentsByName('file'); if ($field->name !== 'originalphoto') return; // Define the custom URL handler for files $customUrlBase = '/photo-file-handler.php?file='; $filePath = $page->id . '/' . urlencode(basename($filename)); $event->return = $customUrlBase . $filePath; } } Approach 2: <?php namespace ProcessWire; /** * FieldtypeCustomImage * Custom fieldtype that stores images outside the web root. */ class FieldtypeOriginalFile extends FieldtypeFile { public static function getModuleInfo() { return [ 'title' => 'Original Image Field', 'version' => 1, 'summary' => 'An original image field that stores images outside the web root.', 'requires' => ['FieldtypeFile', 'InputfieldOriginalFile'], 'icon' => 'image', ]; } /** * Initialize the module and hook into Pages::saved to move files after the page is saved. */ public function init() { parent::init(); $this->addHookAfter('InputfieldFile::processInputFile', $this, 'moveFilesToCustomPath'); //$this->addHookBefore('Pages::saved', $this, 'moveFilesToCustomPath'); // werkt niet, doet niets // werkt niet // $this->set('destinationPath', '/home/portila/originalphotos/'); // Set the custom path for uploaded files // hook into delete page to remove the file from the custom path $this->addHookBefore('Pages::delete', $this, 'removeCustomFile'); // Hook into Pagefile::remove to ensure files are deleted from the custom path $this->addHookBefore('Pagefile::remove', $this, 'removeCustomFile'); } public function moveFilesToCustomPath(HookEvent $event) { $pagefile = $event->argumentsByName('pagefile'); if($pagefile->field->name != 'originalphoto') return; //if($pagefile->filename == '') return; // dit is NOOIT leeg! // dus checken als file bestaat if(!file_exists($pagefile->filename)) return; $customPath = '/home/portila/originalphotos/'; $newPath = $customPath . basename($pagefile->filename); // Move the file from the assets location to the custom path if (rename($pagefile->filename, $newPath)) { // Update the file's path in ProcessWire (not in the database) //$pagefile->filename = $newPath; // Log a message the new path $this->message("The file from the custom path: $newPath"); // log message with current page id $this->message("The page id is: " . $pagefile->page->id); } else { // Log an error if the file couldn't be moved $this->error("Could not move the file to the custom path: $pagefile->filename"); } } public function removeCustomFile(HookEvent $event) { $pagefile = $event->argumentsByName('pagefile'); if($pagefile->field->name != 'originalphoto') return; $customPath = '/home/portila/originalphotos/'; // error if $pagefile->filename is empty if($pagefile->filename == '') { $this->error("The file is empty"); return; } $newPath = $customPath . basename($pagefile->filename); // Check if the file exists if (!file_exists($newPath)) { $this->error("The file does not exist at the custom path: $newPath"); return; } // Remove the file from the custom path if (@unlink($newPath)) { // Log a message if the file was removed $this->message("The file was removed from the custom path: $newPath"); } else { // Log an error if the file couldn't be removed $this->error("Could not remove the file from the custom path: $newPath"); } } } Link to comment Share on other sites More sharing options...
cb2004 Posted November 19, 2024 Share Posted November 19, 2024 https://github.com/wanze/FieldtypeSecureFile Could get some inspiration from there. I think this is a well needed core feature personally. 2 Link to comment Share on other sites More sharing options...
KarlvonKarton Posted November 20, 2024 Author Share Posted November 20, 2024 On 11/19/2024 at 10:34 AM, cb2004 said: a well needed core feature personally. Indeed, that would be neat. 1 Link to comment Share on other sites More sharing options...
KarlvonKarton Posted December 11, 2024 Author Share Posted December 11, 2024 I ended up modifying the module (FieldtypeSecureFile) of Wanze to my needs. Wanze's module doesn't delete the files nor the sub-directories where the files are in. Also, the module didn't work in the latest version of PW. Now it does, but I don't know about the old versions (and I don't care). I left out the download part and the roles, because I don't need that. I'm sure there are some things that could be approved in the code, but it works for me. I can now safely upload and delete files in a directory outside the webroot. Example: /home/username/securedir The link to the module: FieldtypeSecureFile (GitHub) 4 Link to comment Share on other sites More sharing options...
digitalbricks Posted March 10 Share Posted March 10 Thanks for sharing the modified code! Currently working on a project where I also need the ability to store files in a folder not accessible via HTTP, I already applied the fixes from the issues in wanzes repo to my local copy but missed detail that files are not deleted. So great you addressed that. On 12/11/2024 at 3:32 PM, KarlvonKarton said: I left out the download part and the roles, because I don't need that. In your GitHub repository I still see the methods for checking roles and triggering a download. Are they just leftover from the forked repo and do not work as expected in your version anymore or did I misunderstand your quote on that? Link to comment Share on other sites More sharing options...
KarlvonKarton Posted July 11 Author Share Posted July 11 On 3/10/2025 at 8:56 PM, digitalbricks said: In your GitHub repository I still see the methods for checking roles and triggering a download. Are they just leftover from the forked repo and do not work as expected in your version anymore or did I misunderstand your quote on that? Yes, I think indeed that they are leftovers... Sorry for late respons! Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now