Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

DataObjectManager Module /

Discuss the DataObjectManager module, and the related ImageGallery module.

Moderators: martimiz, UncleCheese, Sean, Ed, biapar, Willr, Ingo, swaiba

Uploading files from the front end


Go to End


7 Posts   3560 Views

Avatar
Liquid Edge Creative

Community Member, 56 Posts

19 August 2011 at 12:49pm

Edited: 19/08/2011 12:51pm

Hi team,

Using a combination of Peter Bacon Darwin's Associated Folder module and a useful bit of code from Uncle Cheese, I've built a nice wee Resources page that allows FileDataObjects to be uploaded along with a Title and Description (using Uploadify) and displayed in a table for people to download. All is working fine.

Can anyone show me how I can enable users to upload new Resources from the front end as well as through the CMS?

Here's my ResourcesPage.php:

<?php
class ResourcePage extends Page
{

	   static $db = array(
   		'PageHeading' => 'Varchar(200)',
		
   );
	
	static $has_many = array (
		'Resources' => 'Resource'
	);
	
	 static $icon = "themes/tdg/images/treeicons/bricks";
	
	
	public function getCMSFields()
	{
		$f = parent::getCMSFields();
		
		$manager = new FileDataObjectManager(
			$this, // Controller
			'Resources', // Source name
			'Resource', // Source class
			'Attachment', // File name on DataObject
			array(
				'Name' => 'Name', 
				'Description' => 'Description', 
				'Category' => 'Category'
			), // Headings 
			'getCMSFields_forPopup' // Detail fields (function name or FieldSet object)
			// Filter clause
			// Sort clause
			// Join clause
		);
		
		/*$manager->setUploadFolder('assets/', $this->AssociatedFolder);*/
		
		$manager->setUploadFolder(str_replace('assets/','',$this->AssociatedFolder()->Filename));
			
		$manager->allowUploadFolderSelection();
		
		$manager->setFilter(
			'Category', // Name of field to filter
			'Filter by Category', // Label for filter
			singleton('Resource')->dbObject('Category')->enumValues() // Map for filter (could be $dataObject->toDropdownMap(), e.g.)
		);
		
		// If undefined, all types are allowed. Pass with or without a leading "."		
		$manager->setAllowedFileTypes(array()); 
		
		// Label for the upload button in the popup
		$manager->setBrowseButtonText("Upload"); 
		
		// In grid view, what field will appear underneath the icon. If left out, it defaults to the file title.
		$manager->setGridLabelField('Name'); 
		
		// Plural form of the objects being managed. Used on the "Add" button.
		// If left out, this defaults to [MyObjectName]s
		$manager->setPluralTitle('Resources');
				
		$f->addFieldToTab("Root.Content.Resources", $manager);
		$f->addFieldToTab('Root.Content.Main', new TextField('PageHeading', 'Page Heading Text'), 'Content');

		return $f;
	}

}

Object::add_extension('ResourcePage', 'AssociatedFolderDecorator');

class ResourcePage_Controller extends Page_Controller
{
			
}
?>

Here's the html from my page template that displays the Resources:

<table class="resourceTable">
                    	<tr>
                    		<th class="resourceName">File Name</th>
                    		<th class="resourceDesc">Description</th>
                            <th class="resourceCat">Category</th>
                    		<th class="resourceType">File Type</th>
                            <th class="resourceSize">File Size</th>
                    	</tr>
						<% control Resources %>				
                    	<tr>
                    		<td>
                    			<a href="$Attachment.URL">$Name</a>
                            </td>
                            <td>
								$Description
                            </td>
                            <td>
                            	$Category
                            </td>
                            <td>
                            	$Attachment.FileType
                            </td>
                            <td>
                            	$Attachment.Size
                            </td>
                        </tr>
                            
				<% end_control %>
                </table>

Let me know if it would be useful to give you the Resource.php code (for the Resource DataObject).

Many thanks,

David

Avatar
UncleCheese

Forum Moderator, 4102 Posts

20 August 2011 at 12:48am

This should get you started.

http://www.leftandmain.com/silverstripe-screencasts/2010/08/25/uploadify-tutorial-part-2-working-with-frontend-forms/

---------------
Silverstripe tips, tutorials, screencasts, and more. http://www.leftandmain.com

Avatar
Liquid Edge Creative

Community Member, 56 Posts

23 August 2011 at 4:52pm

Hi UC,

Thanks for the suggestion. I'd already worked through the associated tutorial and tried to implement the code with no success. I've now downloaded your original code and copied that into mine to see if I could get it working, again to no avail. When I try to rebuild the database it just stalls when it gets to my ResourcePage.

I'm a designer rather than a programmer so this is a little over my head, but here are some thoughts:

My ResourcePage has_many Resources and the Resource Data Object has_one Attachment and ResourcePage. By comparison, both your JobApplication data object and JobApplicationPage have has_one Resume and has_many Recommendations. I don't see a stated relationship between your Page and your Data Object?
Does this mean the SaveInto function won't work for me because I don't have an exact match between my form fields and the database fields and relationships in my Data Object?

Also, my DataObject includes a getCMSfields_forPopup function. I'd love to be able to simply call that Popup rather than use a form on the page to add a new Resource DataObject to the page, if that makes sense.

Here's my new ResourcePage.php code:

<?php
class ResourcePage extends Page
{

	   static $db = array(
   		'PageHeading' => 'Varchar(200)'
		
   );
	
	static $has_many = array (
		'Resources' => 'Resource'
	);
	
	 static $icon = "themes/tdg/images/treeicons/bricks";
	
	
	public function getCMSFields()
	{
		$f = parent::getCMSFields();
		
		$manager = new FileDataObjectManager(
			$this, // Controller
			'Resources', // Source name
			'Resource', // Source class
			'Attachment', // File name on DataObject
			array(
				'Name' => 'Name', 
				'Description' => 'Description', 
				'Category' => 'Category'
			), // Headings 
			'getCMSFields_forPopup' // Detail fields (function name or FieldSet object)
			// Filter clause
			// Sort clause
			// Join clause
		);
		
		/*$manager->setUploadFolder('assets/', $this->AssociatedFolder);*/
		
		$manager->setUploadFolder(str_replace('assets/','',$this->AssociatedFolder()->Filename));
			
		$manager->allowUploadFolderSelection();
		
		$manager->setFilter(
			'Category', // Name of field to filter
			'Filter by Category', // Label for filter
			singleton('Resource')->dbObject('Category')->enumValues() // Map for filter (could be $dataObject->toDropdownMap(), e.g.)
		);
		
		// If undefined, all types are allowed. Pass with or without a leading "."		
		$manager->setAllowedFileTypes(array()); 
		
		// Label for the upload button in the popup
		$manager->setBrowseButtonText("Upload"); 
		
		// In grid view, what field will appear underneath the icon. If left out, it defaults to the file title.
		$manager->setGridLabelField('Name'); 
		
		// Plural form of the objects being managed. Used on the "Add" button.
		// If left out, this defaults to [MyObjectName]s
		$manager->setPluralTitle('Resources');
				
		$f->addFieldToTab("Root.Content.Resources", $manager);
		$f->addFieldToTab('Root.Content.Main', new TextField('PageHeading', 'Page Heading Text'), 'Content');

		return $f;
	}

}

Object::add_extension('ResourcePage', 'AssociatedFolderDecorator');

class ResourcePage_Controller extends Page_Controller
{

public function UploadForm() {
		$form = new Form (
			$this,
			"UploadForm",
			new FieldSet (
				new TextField('Name', _t('Resource.NAME','Name')),
				new TextField('Description', _t('Resource.DESCRIPTION','Description')),
				new DropdownField('Category', _t('Resource.CATEGORY','Category'), singleton('Resource')->dbObject('Category')->enumValues()),
				$resourcefile = new FileUploadField('Resources', _t('Resource.ATTACHMENT','Upload a new file')),				
				//$files = new MultipleFileUploadField('File', _t('Resource.ATTACHMENT','Upload several files to this page'))
			),
			new FieldSet (
				new FormAction('doUpload', _t('Resource.UPLOAD','Upload'))
			),
			new RequiredFields('Name','Description','Category','File')
		);
		
		$resourcefile->setUploadFolder(str_replace('assets/','',$this->AssociatedFolder()->Filename));
		
		$resourcefile->allowUploadFolderSelection();
		
		/*$file->setFileTypes(array(
			'pdf',
			'doc'
			'docx',
			'xls',
			'xlsx',
		
		));
		
/*
		$recommendations->uploadOnSubmit();
		$resume->uploadOnSubmit();
*/
		
		return $form;

	}
	
	public function doUpload($data, $form) {
		$form->saveInto($fileupload = new Resource());
		$fileupload->write();
		return array (
			'FileUpload' => $fileupload
		);
 
	}
			
}
?>

Here's my Resource.php Data Object:

<?php 
class Resource extends DataObject
{
	static $db = array (
		'Name' => 'Text',
		'Description' => 'Text',
		'Category' => "Enum('Data, Drafts, Drawings, Reports, Other')"
	);
	
	static $has_one = array (
		'Attachment' => 'File',
		'ResourcePage' => 'ResourcePage'
	);
	
	public function getCMSFields_forPopup()
	{
		return new FieldSet(
			new TextField('Name'),
			new TextareaField('Description'),
			new DropdownField('Category','Category', singleton('Resource')->dbObject('Category')->enumValues()),
			new FileIFrameField('Attachment')
		);
	}
}
?>

Thanks again for your help.

Avatar
Liquid Edge Creative

Community Member, 56 Posts

24 August 2011 at 1:01pm

Righto, further progress with this:

I've called getFrontendFields so the following code is pulling the fields from the DataObject into the Front End for me.

public function UploadForm(){
      $fields = singleton('Resource')->getFrontendFields();
      $actions = new FieldSet(
         new FormAction('doUpload', 'Upload Resource')
      );
	  
	  $fields->removeByName('Associated Folder');

      $validator = NULL;      
      $form = new Form(
         $this,
         'UploadForm',
         $fields,
         $actions,
         $validator
      );
	  
      return $form;

   } 

public function doUpload($data, $form) {
		$form->saveInto($fileupload = new Resource());
		$fileupload->write();
		return array (
			'FileUpload' => $fileupload
		);
 
	}

This is working apart from one thing: The file is being uploaded to the default Uploads folder, not the Associated Folder that is linked with the page.

I've tried to include the following code to set the correct upload folder but it throws a "method does not exist on this class" error no matter where I put it:

$fields->setUploadFolder(str_replace('assets/','',$this->AssociatedFolder()->Filename));

Any suggestions of another method I can use to set the correct upload folder?

Avatar
UncleCheese

Forum Moderator, 4102 Posts

25 August 2011 at 5:41am

You need to run that method on an FileUploadField object. You're trying to run it on a FieldSet.

--------------------
SilverStripe tips, tutorials, screencasts and more: http://www.leftandmain.com

Avatar
Liquid Edge Creative

Community Member, 56 Posts

26 August 2011 at 4:24pm

Thanks UC, yes I figured that out but wasn't able to work out how to turn the appropriate FrontendField into a FileUploadField in order to be able to apply the method.

However, after much hair pulling I've finally got something working... almost! Basically, I've created the FileUploadField within the function, then used a replaceField method to replace the file field from my Resource data object with the new FileUploadField. (Hope I've explained that right.)

The end result is that a new FileDataObject is created and appears in my table, and the file itself is uploaded to the correct folder associated with the page. However, three problems remain:

Firstly, I can click the browse button of the Uploadify field and select a file but when I click the Open button in the dialogue box, the subsequent upload does not appear in the Uploadify field. It still just shows the 'No file uploaded' text, even though the file is uploading correctly.

Secondly, when I check in the CMS to see if my file has uploaded correctly, the file appears in the correct folder but the link to this file appears to be broken in the DataObjectManager.
When I check the download link in the table on my page, the path to the file simply says '...assets/' whereas the correct path should be '...assets/associated-folders/thisAssociatedFolder/thisFileName.pdf'.

Thirdly, I'd like to use the UploadOnSubmit method too, as described in your tutorial, but when I try it the file isn't uploaded at all.

Hopefully you can point me to some solutions! Thanks again for lending me your genius!

Here's my code:

public function UploadForm(){
      $fields = singleton('Resource')->getFrontendFields();
      $actions = new FieldSet(
		 new FormAction('doUpload', 'Upload Resource')
      );
	  
	$resourcefile = new FileUploadField('Resources', _t('Resource.ATTACHMENT','Upload a new file'));
	  
	$resourcefile->setUploadFolder(str_replace('assets/','',$this->AssociatedFolder()->Filename));
	//$resourcefile->uploadOnSubmit();
	
	$fields->removeByName('Associated Folder');
	$fields->replaceField('Attachment', $resourcefile);

      $validator = NULL;      
      $form = new Form(
         $this,
         'UploadForm',
         $fields,
         $actions,
         $validator
      );
	  
      return $form;

   }

public function doUpload($data, $form) {
		$form->saveInto($fileupload = new Resource());
		$fileupload->write();
		return array (
			'FileUpload' => $fileupload
		);
 
	}

Avatar
Liquid Edge Creative

Community Member, 56 Posts

8 September 2011 at 10:12am

Hi UC and community,

Hoping I can get some more input on this issue.

Have just bought a new Mac (with Lion) and for some reason the Uploadify browse button is all of a sudden not functioning at all in Firefox (6.0.1) or Safari (5.1) but still working fine in Chrome, go figure. No changes made to my code at all!

All of the problems in my previous post still apply.

Thanks team.