Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion ModuleConfig.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ component {
*/
"moduleRootPath": getCanonicalPath( getCurrentTemplatePath().replaceNoCase( "/ModuleConfig.cfc", "", "one" ) ),
/**
* The default storage path for all cbwire components.
* The default storage path for file uploads.
* Uses the system temporary directory for security.
*/
"uploadsStoragePath": getCanonicalPath( getTempDirectory() & "/cbwire" ),
/**
* The default storage path for single-file component compilation.
* This must be in the module directory for WireBox to instantiate components.
*/
"storagePath": getCanonicalPath( getCurrentTemplatePath().replaceNoCase( "/ModuleConfig.cfc", "", "one" ) & "/models/tmp" ),
/**
Expand Down
12 changes: 10 additions & 2 deletions interceptors/CBWIRE.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,20 @@ component {
*/
function preReinit() {
local.settings = getSettings();
local.tmpDirectory = local.settings.moduleRootPath & "/models/tmp";


// Clean up single-file component temp directory
local.tmpDirectory = local.settings.storagePath;
if ( directoryExists( local.tmpDirectory ) ) {
directoryDelete( local.tmpDirectory, true );
directoryCreate( local.tmpDirectory );
}

// Clean up file uploads temp directory
local.uploadsTmpDirectory = local.settings.uploadsStoragePath;
if ( directoryExists( local.uploadsTmpDirectory ) ) {
directoryDelete( local.uploadsTmpDirectory, true );
directoryCreate( local.uploadsTmpDirectory );
}
}

/**
Expand Down
6 changes: 3 additions & 3 deletions models/CBWIREController.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ component accessors="true" singleton {
*/
function handleFileUpload( incomingRequest, event ) {
// Determine our storage path for temporary files
local.storagePath = getCanonicalPath( variables.moduleSettings.storagePath );
local.storagePath = getCanonicalPath( variables.moduleSettings.uploadsStoragePath );

// Ensure the storage path exists
if( !directoryExists( local.storagePath ) ){
Expand Down Expand Up @@ -174,10 +174,10 @@ component accessors="true" singleton {
return event.noRender();
}

local.metaPath = getCanonicalPath( variables.moduleSettings.storagePath & "/#local.uuid#.json" );
local.metaPath = getCanonicalPath( variables.moduleSettings.uploadsStoragePath & "/#local.uuid#.json" );

local.metaJSON = deserializeJSON( fileRead( local.metaPath ) );
local.contents = fileReadBinary( getCanonicalPath( variables.moduleSettings.storagePath & "/#local.metaJSON.serverFile#" ) );
local.contents = fileReadBinary( getCanonicalPath( variables.moduleSettings.uploadsStoragePath & "/#local.metaJSON.serverFile#" ) );
event
.sendFile(
file = local.contents,
Expand Down
9 changes: 7 additions & 2 deletions models/Component.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,13 @@ component output="true" accessors="true" {
} );
} else {
var initialState = variables._initialDataProperties;
// Reset individual property
variables.data[ arguments.property ] = initialState[ arguments.property ];
// Reset individual property (only if it exists in initial state)
if ( initialState.keyExists( arguments.property ) ) {
variables.data[ arguments.property ] = initialState[ arguments.property ];
} else {
// Property doesn't exist in initial state, set to empty string
variables.data[ arguments.property ] = "";
}
}
}

Expand Down
42 changes: 41 additions & 1 deletion models/FileUpload.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,51 @@ component {
return variables.meta;
}

/**
* Moves the file from temporary storage to a permanent location.
*
* The metadata file remains in the uploads temp directory to track the upload state,
* while the actual file is moved to the specified permanent location. This allows
* the FileUpload object to continue tracking the file even after it's been stored.
*
* @path The destination path where the file should be stored (can be a directory or full file path)
* @return The absolute path to the stored file
*/
function store( required string path ){
var destinationPath = getCanonicalPath( arguments.path );

// Check if destination is a directory
if ( directoryExists( destinationPath ) ) {
destinationPath = getCanonicalPath( destinationPath & "/" & variables.meta.serverFile );
}

// Ensure the destination directory exists
var destinationDir = getDirectoryFromPath( destinationPath );
if ( !directoryExists( destinationDir ) ) {
directoryCreate( destinationDir, true );
}

// Move the file from temporary to permanent location
fileMove( variables.temporaryStoragePath, destinationPath );

// Update the temporary storage path to the new location
variables.temporaryStoragePath = destinationPath;

// Update metadata to reflect new file location
variables.meta.serverDirectory = destinationDir;
variables.meta.serverFile = getFileFromPath( destinationPath );

// Update the metadata file (stays in temp directory for upload tracking)
fileWrite( getMetaPath(), serializeJSON( variables.meta ) );

return destinationPath;
}

/**
* Returns the path to the temp directory (mockable in tests)
*/
function getUploadTempDirectory(){
return getCanonicalPath( variables.moduleSettings.moduleRootPath & "/models/tmp" );
return getCanonicalPath( variables.moduleSettings.uploadsStoragePath );
}

/**
Expand Down
1 change: 0 additions & 1 deletion test-harness/resources/fileupload_metadata.json

This file was deleted.

Binary file added test-harness/tests/resources/logo_test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 9 additions & 2 deletions test-harness/tests/specs/CBWIRESpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ component extends="coldbox.system.testing.BaseTestCase" {

function beforeAll() {
super.beforeAll();
// delete any files in models/tmp folder
// delete any files in models/tmp folder (for single-file components)
local.tempFolder = expandPath( "../../../models/tmp" );
if ( directoryExists( local.tempFolder ) ) {
directoryDelete( local.tempFolder, true );
}
directoryCreate( local.tempFolder );

// Clean out uploads temp directory
local.uploadsTempFolder = getTempDirectory() & "/cbwire";
if ( directoryExists( local.uploadsTempFolder ) ) {
directoryDelete( local.uploadsTempFolder, true );
}
directoryCreate( local.uploadsTempFolder );
}

// Lifecycle methods and BDD suites as before...
Expand Down Expand Up @@ -1077,7 +1084,7 @@ component extends="coldbox.system.testing.BaseTestCase" {
{
"path": "",
"method": "_uploadErrored",
"params": [ "photo", null, false ]
"params": [ "photo", nullValue(), false ]
}
],
updates = {}
Expand Down
114 changes: 104 additions & 10 deletions test-harness/tests/specs/FileUploadSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ component extends="coldbox.system.testing.BaseTestCase" {
super.beforeAll();

// Clean out tmp directory before all tests
local.tempFolder = expandPath( "../../../models/tmp" );
// Use system temp directory + cbwire subdirectory
local.tempFolder = getTempDirectory() & "/cbwire";
if ( directoryExists( local.tempFolder ) ) {
directoryDelete( local.tempFolder, true );
}
Expand Down Expand Up @@ -81,7 +82,7 @@ component extends="coldbox.system.testing.BaseTestCase" {

it( "should return the temporary storage path", function() {
var result = loadMockedFileUpload( "test", "text", "plain" );
expect( result.getTemporaryStoragePath() ).toBe( expandPath( "./resources/logo.png" ) );
expect( result.getTemporaryStoragePath() ).toBe( expandPath( "./resources/logo_test.png" ) );
});

it( "should return binary file contents when calling get", function() {
Expand All @@ -99,17 +100,95 @@ component extends="coldbox.system.testing.BaseTestCase" {
expect( result.getBase64Src() ).toInclude( "data:" );
});

it( "should return correct upload temp directory path with proper slash separation", function() {
it( "should return correct upload temp directory path using system temp directory", function() {
// Create a fresh FileUpload instance (not mocked) to test getUploadTempDirectory
var fileUploadInstance = getInstance( "FileUpload@cbwire" );
var tempDir = fileUploadInstance.getUploadTempDirectory();

// The path should contain "/models/tmp" with proper slash separation
expect( tempDir ).toInclude( "/models/tmp" );
// Should not have malformed concatenation like "wiremodels"
expect( tempDir ).notToInclude( "wiremodels" );
// Should end with models/tmp
expect( right( tempDir, 10 ) ).toBe( "models/tmp" );
// The path should end with /cbwire
expect( tempDir ).toInclude( "/cbwire" );
// Should use the system temp directory
expect( tempDir ).toInclude( getTempDirectory() );
});

it( "should store file to a specified directory path", function() {
var result = loadMockedFileUpload( "test-store", "text", "plain" );
var destinationDir = getTempDirectory() & "/cbwire-test-store";

// Ensure the test directory exists
if ( !directoryExists( destinationDir ) ) {
directoryCreate( destinationDir );
}

// Store the file
var storedPath = result.store( destinationDir );

// Verify the file was moved to the destination
expect( fileExists( storedPath ) ).toBeTrue();
// Use getCanonicalPath to normalize the path for comparison
expect( storedPath ).toInclude( getCanonicalPath( destinationDir ) );
expect( storedPath ).toInclude( "logo_test-store.png" );

// Clean up
if ( directoryExists( destinationDir ) ) {
directoryDelete( destinationDir, true );
}
});

it( "should store file to a specified file path", function() {
var result = loadMockedFileUpload( "test-store-file", "text", "plain" );
var destinationDir = getTempDirectory() & "/cbwire-test-store-file";
var destinationPath = destinationDir & "/myfile.png";

// Store the file with specific filename
var storedPath = result.store( destinationPath );

// Verify the file was moved to the destination with the new name
expect( fileExists( storedPath ) ).toBeTrue();
expect( storedPath ).toBe( getCanonicalPath( destinationPath ) );

// Clean up
if ( directoryExists( destinationDir ) ) {
directoryDelete( destinationDir, true );
}
});

it( "should update temporary storage path after store", function() {
var result = loadMockedFileUpload( "test-store-update", "text", "plain" );
var destinationDir = getTempDirectory() & "/cbwire-test-store-update";

// Store the file
var storedPath = result.store( destinationDir );

// Verify the temporary storage path was updated
expect( result.getTemporaryStoragePath() ).toBe( storedPath );

// Clean up
if ( directoryExists( destinationDir ) ) {
directoryDelete( destinationDir, true );
}
});

it( "should be able to destroy file after store", function() {
var result = loadMockedFileUpload( "test-store-destroy", "text", "plain" );
var destinationDir = getTempDirectory() & "/cbwire-test-store-destroy";

// Store the file
var storedPath = result.store( destinationDir );

// Verify file exists at new location
expect( fileExists( storedPath ) ).toBeTrue();

// Now destroy should delete from the new location
result.destroy();

// Verify file was deleted
expect( fileExists( storedPath ) ).toBeFalse();

// Clean up directory
if ( directoryExists( destinationDir ) ) {
directoryDelete( destinationDir, true );
}
});

});
Expand All @@ -129,12 +208,27 @@ component extends="coldbox.system.testing.BaseTestCase" {
) {
var metaPath = expandPath( "../resources/fileupload_metadata.json" );

// Create a unique copy of logo.png for this test instance
// This ensures each test that calls store() has its own file to move
var uniqueFileName = "logo_" & arguments.uuid & ".png";
var sourcePath = expandPath( "./resources/logo.png" );
var destinationPath = expandPath( "./resources/" & uniqueFileName );

// Always create a fresh copy for each test
if ( fileExists( sourcePath ) ) {
// Delete destination if it exists (from previous test run)
if ( fileExists( destinationPath ) ) {
fileDelete( destinationPath );
}
fileCopy( sourcePath, destinationPath );
}

writeTestMetaFile(
path = metaPath,
data = {
"uuid" : arguments.uuid,
"serverDirectory" : expandPath( "./resources" ),
"serverFile" : "logo.png",
"serverFile" : uniqueFileName,
"contentType" : arguments.contentType,
"contentSubType" : arguments.contentSubType,
"fileSize" : 1234
Expand Down