Aaron Ahearne Posted November 11, 2015 Share Posted November 11, 2015 Hi guys, I have a requirement for a new input field for Form Builder. Essentially I just need to have some radio buttons which use an image as their label. I have managed to create the module, have it install automatically and create a new form with my custom input type but I am having trouble handling the image which is being uploaded as the label. The value for imageLabel is always an empty array. I have looked all around the forums and I Google but I cant find the answer close to what Im looking for. Do I handle the file upload manually and extract/add it to my imageField during POST and GET? I've tried multiple different methods and been through most of the API too but can't figure out the correct way to do this. <?php class InputfieldImageRadios extends InputfieldRadios { const CONFIGNAME = "ImageRadios"; public static function getModuleInfo() { return array( 'title' => __('Radio Buttons with Image Label', __FILE__), 'summary' => __('Radio buttons for selection of a single item with an extra image label', __FILE__), 'version' => 1, 'singular' => true, 'autoload' => true, 'requires' => array('InputfieldRadios', "FormBuilder") ); } public function init() { parent::init(); } public function ___getConfigInputFields() { $inputFields = parent::___getConfigInputfields(); $imageField = wire('modules')->get('InputfieldFile'); $imageField->label = $this->_('Image Label'); $imageField->description = $this->_('If you want the label to be displayed as an image.'); $imageField->extensions = 'jpg jpeg png gif'; $imageField->maxFiles = 1; $imageField->maxFileSize = 200000; $imageField->overwrite = false; $imageField->destinationPath = wire("config")->paths->files . "radioLabels/"; $imageField->name = "imageLabel"; $imageField->type = 'file'; $imageField->value = array(); $inputFields->add($imageField); return $inputFields; } public function set($key, $value) { if($key == 'imageLabel') { $test = 1; } return parent::set($key, $value); } /** * Install the module append to Form Builder config * */ public function ___install() { $formBuilderClass = "FormBuilder"; $this->verifyDependencies([$formBuilderClass]); $formBuilderConfig = wire("modules")->getModuleConfigData($formBuilderClass); if (in_array(self::CONFIGNAME, $formBuilderConfig['inputfieldClasses'])) { return; } $formBuilderConfig['inputfieldClasses'][] = self::CONFIGNAME; wire("modules")->saveModuleConfigData($formBuilderClass, $formBuilderConfig); } protected function verifyDependencies($classes) { foreach ($classes as $class) { if (!wire('modules')->isInstalled($class)) { $this->error(self::CONFIGNAME . " dependency not installed: $class"); } } } /** * Uninstall the module and remove from Form Builder config * */ public function ___uninstall() { $formBuilderClass = "FormBuilder"; $this->verifyDependencies([$formBuilderClass]); $formBuilderConfig = wire("modules")->getModuleConfigData($formBuilderClass); foreach ($formBuilderConfig['inputfieldClasses'] as $key => $value) { if ($value == self::CONFIGNAME) { unset($formBuilderConfig['inputfieldClasses'][$key]); } } wire("modules")->saveModuleConfigData($formBuilderClass, $formBuilderConfig); } } Link to comment Share on other sites More sharing options...
LostKobrakai Posted November 11, 2015 Share Posted November 11, 2015 You'd need to do it manually e.g. as part of the module or a tad more sophisticated. The InputfieldFile does actually handle only the "input" stuff, e.g. everything till you hit submit on the form (or ajax). Everything after that is handled by FieldtypeFile, which saves the file for a specific page. That's the reason why FormBuilder does have it's own File field, as there's neigher a FieldtypeFile to be used nor a page to save images to. With that being said, have a look at Ryan's reply here about how to handle file uploads even without the Fieldtype: https://processwire.com/talk/topic/11314-inputfieldfile-in-forms-its-just-not-working/?hl=file#entry106503 Link to comment Share on other sites More sharing options...
Aaron Ahearne Posted November 11, 2015 Author Share Posted November 11, 2015 Great thanks, thats all I needed to know. I kept getting the error: Call to a member function add() on a non-object (line 373 of /html/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module). I suppose thats because it is expecting an entry in the image table, but it hasnt created one as it has no page to reference. That makes a bit more sense now. Now that Im using the correct Input File module it should be more straight forward. I'll try to set up some hooks to perform after it processes the file and I'll post my solution here once its done. Link to comment Share on other sites More sharing options...
LostKobrakai Posted November 11, 2015 Share Posted November 11, 2015 Just to prevent misunderstandings. You're already using the "correct" InputfieldFile as the FormBuilder custom one is even more specific. Also the add() method didn't fail because of a missing table, but just because of the missing page. The page itself doesn't necessarily need an file field, as Ryan also stated, but mostly it's id is needed to determine the folder to put the files into. 1 Link to comment Share on other sites More sharing options...
Aaron Ahearne Posted November 12, 2015 Author Share Posted November 12, 2015 Nothing I have tried seems to be working. I cannot use hooks as it will hook every time InputFieldFile is run... unless there is a way to set the hook only when my module is run initialized (setting them in init caused them to run regardless of whether the module was initialized) I just cannot seem to get a value into InputFieldFile. It is always blank. wire('input')->post does not seem to contain any files at all. Even though a file is uploaded to the server. wire('input')->post = {WireInputData} [2] stripSlashes = false data = {array} [16] field_name = "aaa" field_type = "ImageRadios" field_label = "aaa" description = "" notes = "" collapsed = "0" showIf = "" columnWidth = "100%" requiredIf = "" defaultValue = "" options = "Option 1\r\nOption 2\r\nOption 3" optionColumns = "0" _form_id = "4" _field_name = "aaa" _submit_save_field = "Save" TOKEN1788870938X1447321212 = "NmaT7RliGuwEUVaGABrADx/YBdZTiKVf" Link to comment Share on other sites More sharing options...
LostKobrakai Posted November 12, 2015 Share Posted November 12, 2015 You can use hooks by creating a custom property in the inputfield object and in the hook just check for that property. You're writing you cannot set a value so I would conclude that the upload does already work. Am I right that way? For setting the value to the inputfield make sure the value is a page pagefiles object or at least an array of pagefile objects. Link to comment Share on other sites More sharing options...
Aaron Ahearne Posted November 12, 2015 Author Share Posted November 12, 2015 I tried manually setting the value of InputFieldFile using a beforeHook on ___processInputAddFile and although it was showing as an array of pagefiles, I was still receiving errors saying that it was attempting to call first() a non-object. Strangely it is showing literally Array[Pagefiles], rather than an instance of WireArray or just a root of page files. Obviously a normal array does not have a first() function, but by creating it manually it should definitely have access to the first() function defined in WireArray. $this->value = new Pagefiles(wire("pages")->get("/offers")); This is not the actual name of my page btw, as Im using the FormBuilder page, so Im just using this page to force something into the db. Link to comment Share on other sites More sharing options...
Aaron Ahearne Posted November 12, 2015 Author Share Posted November 12, 2015 I think this was an issue to do with the setters in InputfieldFile. As the value is being set as an array, any further setters to value are being set as values in that array. Its just a matter of finding out what is setting it. Link to comment Share on other sites More sharing options...
LostKobrakai Posted November 12, 2015 Share Posted November 12, 2015 I'm not sure about the current state of your module, but one thing you need to concider is that files are never part of neither $_POST nor $input->post. Uploaded files can be retrieved from the $_FILES superglobal or you just use the WireUpload class. Link to comment Share on other sites More sharing options...
Aaron Ahearne Posted November 17, 2015 Author Share Posted November 17, 2015 To wrap up this subject, I wanted to post some of the code required to handle the file upload. Please let me know if you see any glaring mistakes. I added several hooks to modify the data going into and coming out of ProcessInput for InputfieldFile, that way I managed to grab the file name and store it as part of my form input config in the forms table. Then, when retrieving the data, I had the final part of the file path stored so I could access the image. Unfortunately this does mean that I will need to implement the delete and description functionality of the file uploader myself. public function init() { parent::init(); $this->set("image", ''); $this->addHookBefore("InputfieldFile::processInput", $this, "beforeProcessInput"); $this->addHookAfter("InputfieldFile::processInput", $this, "afterProcessInput"); $this->addHookAfter("InputfieldFile::processInputAddFile", $this, "afterProcessInputAddFile"); } /** * Modify the value property of the InputFieldFile. This sets the form admin page as the value so as to * bypass an error when saving a new image. * @param HookEvent $event */ public function beforeProcessInput(HookEvent $event) { if (!$this->isImageLabelEvent($event)) { return; } $event->object->removeAttr("value"); // sanitise the value field $event->object->set("value", new Pagefiles($this->wire('page'))); } /** * Save the form image to its own directory and save the image path as part of the input field config * @param HookEvent $event */ public function afterProcessInput(HookEvent $event) { if (!$this->isImageLabelEvent($event)) { return; } $uploadedFiles = $event->object->getWireUpload()->getCompletedFilenames(); if (empty($uploadedFiles)) { return; } $formId = wire("input")->get("id"); $form = wire("forms")->get($formId); $imageLabelFieldName = wire("input")->post("field_name"); $imageRadioField = $form->get("children")[$imageLabelFieldName]; $imageFolder = self::DIR_PREFIX . $formId; if ($imageRadioField) { if (!file_exists($imageFolder)) { mkdir($imageFolder); } $imageRadioField->set("image", $imageFolder . "/" . $uploadedFiles[0]); $form->save(); } } /** * Delete the current image once a new one is uploaded * @param HookEvent $event */ public function afterProcessInputAddFile(HookEvent $event) { if (!$this->isImageLabelEvent($event)) { return; } $uploadedFiles = $event->object->getWireUpload()->getCompletedFilenames(); if (empty($uploadedFiles)) { return; } $imagePath = $this->getCurrentImagePath(); if ($imagePath) { unlink($imagePath); } } /** * Create the file input to allow users to override the default series image * @return mixed */ private function createInputFieldFile() { $formId = wire("input")->get("id"); $inputFieldFile = wire('modules')->get('InputfieldFile'); $inputFieldFile->label = $this->_('Image Label'); $inputFieldFile->description = $this->_('Override the series default image.'); $inputFieldFile->extensions = 'jpg jpeg png gif'; $inputFieldFile->maxFiles = 1; $inputFieldFile->maxFileSize = 200000; $inputFieldFile->overwrite = false; $inputFieldFile->destinationPath = $this->getFormImageFilePath($formId); $inputFieldFile->attr('name', self::IMAGE_LABEL_NAME); $inputFieldFile->type = "file"; if ($this->getCurrentImagePath()) { $pageFiles = new Pagefiles($this->wire('page')); $pageFile = new Pagefile($pageFiles, $this->getCurrentImagePath()); $pageFiles->add($pageFile); $inputFieldFile->set('value', $pageFiles); } return $inputFieldFile; } Link to comment Share on other sites More sharing options...
eangulo@spiria Posted October 21, 2016 Share Posted October 21, 2016 Hello, (sorry for my english! ) I created a custom file field input (InputfieldFile) on my custom XML importer process module, but when I select a file my field do not show information about the selected file before upload. InputfieldFile code: $file = $this->modules->get("InputfieldFile"); $file->name = 'upload_xml_file'; $file->id = 'field_upload_xml_file'; $file->inputfieldClass = 'InputfieldFile'; $file->label = $this->_('Upload XML File'); $file->extensions = 'xml natfis'; $file->destinationPath = $this->destinationPath; $file->maxFiles = 1; $file->required = 1; $file->type = "file"; $file->icon = 'fa-upload'; $file->attr('onchange', 'return validateFileExtension(this)'); // validate filename $fieldset->add($file); Actually (custom inputfieldFile): What I want (field file created on fields administration): Thank you very much! Link to comment Share on other sites More sharing options...
adrian Posted October 31, 2016 Share Posted October 31, 2016 Is this related: https://github.com/processwire/processwire-issues/issues/38 Take a look at this rough fiddle: http://jsfiddle.net/hw5t2xcu/ 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