4

I have a custom component that takes input from the admin and displays it as a custom styled view on the front-end of the site. One of the fields is a file upload for an image and I cannot figure out/find online how to link the controller upload code to the view. Everything I've read online basically says, "Study existing core component code" and I've done that to no avail.

My admin controller:

class NewsShowcaseController extends JController
{
    /**
     * display task
     *
     * @return void
     */
    function display($cachable = false, $urlparams = false) 
    {
            // set default view if not set
            $input = JFactory::getApplication()->input;
            $input->set('view', $input->getCmd('view', 'NewsShowcaseAdmin'));

            // call parent behavior
            parent::display($cachable);
    }

    /*
     * The folder we are uploading into
     */
    protected $folder = '';

    /**
     * Upload one or more files
     *
     * @since 1.5
     */
    public function upload()
    {
            // Check for request forgeries
            JSession::checkToken('request') or jexit(JText::_('JINVALID_TOKEN'));
            $params = JComponentHelper::getParams('com_media');
            // Get some data from the request
            $files                  = JRequest::getVar('Filedata', '', 'files', 'array');
            $return                 = JRequest::getVar('return-url', null, 'post', 'base64');
            $this->folder   = JRequest::getVar('folder', '', '', 'path');

            // Set the redirect
            if ($return)
            {
                    $this->setRedirect(base64_decode($return) . '&folder=' . $this->folder);
            }

            // Authorize the user
            if (!$this->authoriseUser('create'))
            {
                    return false;
            }
            if (
                    $_SERVER['CONTENT_LENGTH']>($params->get('upload_maxsize', 0) * 1024 * 1024) ||
                    $_SERVER['CONTENT_LENGTH']>(int)(ini_get('upload_max_filesize'))* 1024 * 1024 ||
                    $_SERVER['CONTENT_LENGTH']>(int)(ini_get('post_max_size'))* 1024 * 1024 ||
                    (($_SERVER['CONTENT_LENGTH'] > (int) (ini_get('memory_limit')) * 1024 * 1024) && ((int) (ini_get('memory_limit')) != -1))
            )
            {
                    JError::raiseWarning(100, JText::_('COM_MEDIA_ERROR_WARNFILETOOLARGE'));
                    return false;
            }
            // Input is in the form of an associative array containing numerically indexed arrays
            // We want a numerically indexed array containing associative arrays
            // Cast each item as array in case the Filedata parameter was not sent as such
            $files = array_map( array($this, 'reformatFilesArray'),
                    (array) $files['name'], (array) $files['type'], (array) $files['tmp_name'], (array) $files['error'], (array) $files['size']
            );

            // Perform basic checks on file info before attempting anything
            foreach ($files as &$file)
            {
                    if ($file['error']==1)
                    {
                            JError::raiseWarning(100, JText::_('COM_MEDIA_ERROR_WARNFILETOOLARGE'));
                            return false;
                    }
                    if ($file['size']>($params->get('upload_maxsize', 0) * 1024 * 1024))
                    {
                            JError::raiseNotice(100, JText::_('COM_MEDIA_ERROR_WARNFILETOOLARGE'));
                            return false;
                    }

                    if (JFile::exists($file['filepath']))
                    {
                            // A file with this name already exists
                            JError::raiseWarning(100, JText::_('COM_MEDIA_ERROR_FILE_EXISTS'));
                            return false;
                    }

                    if (!isset($file['name']))
                    {
                            // No filename (after the name was cleaned by JFile::makeSafe)
                            $this->setRedirect('index.php', JText::_('COM_MEDIA_INVALID_REQUEST'), 'error');
                            return false;
                    }
            }

            // Set FTP credentials, if given
            JClientHelper::setCredentialsFromRequest('ftp');
            JPluginHelper::importPlugin('content');
            $dispatcher     = JDispatcher::getInstance();

            foreach ($files as &$file)
            {
                    // The request is valid
                    $err = null;
                    if (!MediaHelper::canUpload($file, $err))
                    {
                            // The file can't be upload
                            JError::raiseNotice(100, JText::_($err));
                            return false;
                    }

                    // Trigger the onContentBeforeSave event.
                    $object_file = new JObject($file);
                    $result = $dispatcher->trigger('onContentBeforeSave', array('com_media.file', &$object_file, true));
                    if (in_array(false, $result, true))
                    {
                            // There are some errors in the plugins
                            JError::raiseWarning(100, JText::plural('COM_MEDIA_ERROR_BEFORE_SAVE', count($errors = $object_file->getErrors()), implode('<br />', $errors)));
                            return false;
                    }

                    if (!JFile::upload($file['tmp_name'], $file['filepath']))
                    {
                            // Error in upload
                            JError::raiseWarning(100, JText::_('COM_MEDIA_ERROR_UNABLE_TO_UPLOAD_FILE'));
                            return false;
                    }
                    else
                    {
                            // Trigger the onContentAfterSave event.
                            $dispatcher->trigger('onContentAfterSave', array('com_media.file', &$object_file, true));
                            $this->setMessage(JText::sprintf('COM_MEDIA_UPLOAD_COMPLETE', substr($file['filepath'], strlen(COM_MEDIA_BASE))));
                    }
            }

            return true;
    }

}

I took the upload code from the core com_media component's "file" controller.

My view:

   class NewsShowcaseViewNewsShowcaseAdmin extends JView
   {
/**
 * News Showcase Admin view display method
     * @return void
     */
    function display($tpl = null) 
    {
            // Get data from the model
            $items = $this->get('Items');
            $pagination = $this->get('Pagination');
            $this->image = $this->get('Image');
            $this->description = $this->get('Description');

            // Check for errors.
            if (count($errors = $this->get('Errors'))) 
            {
                    JError::raiseError(500, implode('<br />', $errors));
                    return false;
            }
            // Assign data to the view
            $this->items = $items;
            $this->pagination = $pagination;

            // Display the template
            parent::display($tpl);
    }

}

HTML:

            <form name="upload" method="post" enctype="multipart/form-data">
                <input type="file" name="file_upload" />
                <input type="submit" value="Upload Image" />
            </form>

I didn't include any code for the model because I assume that's the piece I am missing? I am unsure of how to go about coding it though. I have some "get" methods in my model, but would this be a "set" method? Or how do I call a function from the controller and pass it the input from the HTML/view?

  • What version of Joomla are you doing this in? – David Fritsch Sep 20 '14 at 21:13
  • Also, what are you ultimately hoping to happen? Just save the form data and direct back to the form? Go somewhere else? Update the database? – David Fritsch Sep 20 '14 at 21:14
  • 2.5, and upload the image and store the url in the database. Upon upload it should direct back to the form since there will be more fields to fill out. There is a preview feature though which is why it would be ideal to upload then fill out the rest of the fields. – Eric Sep 20 '14 at 21:19
  • One more question than I should be able to answer things, when you currently submit the form, does the upload function in your controller get called or does it just display the form again? – David Fritsch Sep 21 '14 at 1:38
  • Just displays the form again because I haven't associated it, or rather don't know how I should go about associating it, with the controller – Eric Sep 21 '14 at 1:47
4

To start, it would probably be best to go through all of this: http://docs.joomla.org/J2.5:Developing_a_MVC_Component/Developing_a_Basic_Component. It's a lot of information, but it will definitely help answer a lot of your questions.


Let's start with the form. There are a few pieces that you can do to get this to point at the controller function:

<form name="upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file_upload" />
    <input type="submit" value="Upload Image" />
    <input type="hidden" name="option" value="com_component_name" />
    <input type="hidden" name="task" value="controller.method" />
    <?php echo JHtml::_( 'form.token' ); ?>
</form>

I've added two hidden inputs to the form. These two fields route the posted data. The first one should match your component. The second can either be just a method name (if you are routing to the base controller.php file in the root of your component) or it should use this "dot syntax" to specify the controller and method. (Your case would probably just be upload since that looks like the base controller to me.)

The extra php bit at the end adds a CSRF token to help prevent CSRF attacks. You can read more about it here: http://docs.joomla.org/How_to_add_CSRF_anti-spoofing_to_forms. The part to check for this token is already at the top of the upload function.


Now that you are actually getting your upload function to run on the form submission, we need to process the file.

Don't bother using the JRequest class for input. It is deprecated and will go away soon. Use JInput as outlined here: http://docs.joomla.org/Retrieving_request_data_using_JInput.

Pretty much everything before the following line isn't probably necessary for the task at hand. It allows customization that you don't really need for now:

$files = array_map( array($this, 'reformatFilesArray'),
    (array) $files['name'], (array) $files['type'], (array) $files['tmp_name'], (array) $files['error'], (array) $files['size']
);

Because we are using JInput now, we can instead do this like so:

$app = JFactory::getApplication();
$files = $app->input->files->get('file_upload');

I believe all of the rest of the interating on the $files array should work. You will want to check whether $files is an array of files or just a single level array of a file. In the latter case, you don't need the foreach.


That should be enough for this question. I can almost guarantee that you will have more questions. This question is already fairly broad, so don't keep asking pieces onto this. Instead, please feel free to open new questions on the specific problems that you are having (as well as searching more specifically on those problems, because there are a lot of resources on this already.)

  • Thanks for all of your help! I will be opening a new question on JInput as I implemented as you said and as stated in the documentation you provided and it isn't working. – Eric Sep 23 '14 at 1:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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