Best method for single image upload/display on custom part

Topics: Writing modules
Jul 10, 2012 at 6:30 PM

I've been looking through tutorials and other modules at examples and wanted to know opinions on the best method for doing this.

I have a custom part named ShowPart. I need to be able to upload a single logo for each Show item that is created (on the front end of the site, not Admin) and also be able to display it.

I've been going through the Avatars module to see how it's done there, but was wondering if it would be just as easy to attach a Media Picker field to the ShowPart in my Migrations file? Would I get the same results? I saw this example in the WebShop tutorial:

ContentDefinitionManager.AlterPartDefinition(typeof(AddressPart).Name, p => p
                .Attachable(false)
 .WithField("Name", f => f.OfType(typeof(TextField).Name))
                .WithField("AddressLine1", f => f.OfType(typeof(TextField).Name))
                .WithField("AddressLine2", f => f.OfType(typeof(TextField).Name))
                .WithField("Zipcode", f => f.OfType(typeof(TextField).Name))
                .WithField("City", f => f.OfType(typeof(TextField).Name))
                .WithField("Country", f => f.OfType(typeof(TextField).Name))
                );

and thought it might be easier just to attach the field.

Any thoughts on the best approach for this? Thanks.

Jul 10, 2012 at 6:54 PM
There's Contrib.ImageField - sounds like it should do what you're looking for.

Cheers, Oliver
--


2012/7/10 psenechal <notifications@codeplex.com>

From: psenechal

I've been looking through tutorials and other modules at examples and wanted to know opinions on the best method for doing this.

I have a custom part named ShowPart. I need to be able to upload a single logo for each Show item that is created (on the front end of the site, not Admin) and also be able to display it.

I've been going through the Avatars module to see how it's done there, but was wondering if it would be just as easy to attach a Media Picker field to the ShowPart in my Migrations file? Would I get the same results? I saw this example in the WebShop tutorial:

ContentDefinitionManager.AlterPartDefinition(typeof(AddressPart).Name, p => p
                .Attachable(false)
 .WithField("Name", f => f.OfType(typeof(TextField).Name))
                .WithField("AddressLine1", f => f.OfType(typeof(TextField).Name))
                .WithField("AddressLine2", f => f.OfType(typeof(TextField).Name))
                .WithField("Zipcode", f => f.OfType(typeof(TextField).Name))
                .WithField("City", f => f.OfType(typeof(TextField).Name))
                .WithField("Country", f => f.OfType(typeof(TextField).Name))
                );

and thought it might be easier just to attach the field.

Any thoughts on the best approach for this? Thanks.

Read the full discussion online.

To add a post to this discussion, reply to this email (orchard@discussions.codeplex.com)

To start a new discussion for this project, email orchard@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com


Jul 11, 2012 at 6:29 AM

I actually decided to go the way of Profile.Avatars to upload the image. I've got everything working except for one small detail and I think I might know what the problem is, just not how to solve it.

in my driver, I have the following

        // POST
        protected override DriverResult Editor(ShowPart part, IUpdateModel updater, dynamic shapeHelper) {
            updater.TryUpdateModel(part, Prefix, null, null);

            var postedFile = ((Controller)updater).Request.Files["Shows_FileUpload"];
            if (postedFile != null && !String.IsNullOrEmpty(postedFile.FileName)) {
                _showService.SaveImageFile(part, postedFile);
                updater.TranscribeValidationDictionaryErrors<ShowServiceValidationKey>(_showService.ValidationDictionary);
            }

            return Editor(part, shapeHelper);
        }

which calls _showService.SaveImageFile and ends up here

        public bool SaveImageFile(int id, Stream stream, string extension) {
            extension = extension.StripExtension();
            var settings = GetSettings();

            if (stream.Length > settings.MaxFileSize) {
                ValidationDictionary.AddError(ShowServiceValidationKey.FileTooLarge, T("The file was too large for a logo ({0}KB), maximum file size is {1}KB",
                    Math.Round((float)(stream.Length / 1024)),
                    Math.Round((float)(settings.MaxFileSize / 1024))));

                return false;
            }

            var filePath = GetFilePath(id, extension);

            if (!IsFileAllowed(filePath)) {
                ValidationDictionary.AddError(ShowServiceValidationKey.NotAllowedFileType, T("This file type is not allowed as a logo"));

                return false;
            }

            // This is the way to overwrite a file
            try {
                _storageProvider.DeleteFile(filePath);
            }
            catch (Exception) {
            }

            _storageProvider.SaveStream(filePath, stream);

            var show = _contentManager.Get<ShowPart>(id);
            show.FileExtension = extension;
            _contentManager.Flush();

            return true;
        }

The problem is occurring towards the end in the line

var show = _contentManager.Get<ShowPart>(id);

show is coming back as null. I debugged and discovered that...even though id is the correct id. What happens is that the record for the show gets written, but the FileExtension is NULL.

In the Avatars module, the User content item that is being retrieved has already been created. I'm wondering if at this point in my code though, the Show item has not been created and therefore cannot be retrieved using _contentManager.Get(id).

Does this sound like a correct diagnosis? If so...is there a way I can get the Show item being created differently or maybe re-order something so that the Show item gets created first before trying to add the FileExtension?

Thanks for any help you can provide.

Jul 11, 2012 at 7:10 AM

I think I found where the issue is...it's actually in the Controller

        [HttpPost, ActionName("Create")]
        public ActionResult CreatePOST() {
            var show = _orchardServices.ContentManager.Create<ShowPart>(ContentPartConstants.Show, VersionOptions.Draft);

            dynamic shape = _orchardServices.ContentManager.UpdateEditor(show, this);

            if (!ModelState.IsValid) {
                _orchardServices.TransactionManager.Cancel();
                return View((object)shape);
            }

            _orchardServices.ContentManager.Publish(show.ContentItem);

            _orchardServices.Notifier.Information(T("Show created."));

            return RedirectToAction("List");
        }

I'm not actually publishing the content item until after I run the _showService.SaveImageFile method.

If I move the Publish line above the UpdateEditor call, it works fine...but if I do this, I'm trying to publish the content item before I've checked if the model is valid.

What would be the best way to reorder this to work the way I need it? Thanks again!

Jul 11, 2012 at 7:20 AM

Am I okay to design my Controller POST action as such?

        [HttpPost, ActionName("Create")]
        public ActionResult CreatePOST() {
            var show = _orchardServices.ContentManager.Create<ShowPart>(ContentPartConstants.Show, VersionOptions.Draft);

            if (!ModelState.IsValid) {
                dynamic shape = _orchardServices.ContentManager.UpdateEditor(show, this);
                _orchardServices.TransactionManager.Cancel();
                return View((object)shape);
            }

            _orchardServices.ContentManager.Publish(show.ContentItem);

            _orchardServices.ContentManager.UpdateEditor(show, this);

            _orchardServices.Notifier.Information(T("Show created."));

            return RedirectToAction("List");
        }

Coordinator
Jul 11, 2012 at 2:14 PM
Edited Jul 11, 2012 at 2:15 PM

What is UpdateEditor supposed to do here?

Jul 11, 2012 at 4:10 PM

I was under the impression that it would try to run the method in the driver...is that not the case?

        // POST
        protected override DriverResult Editor(ShowPart part, IUpdateModel updater, dynamic shapeHelper) {
            updater.TryUpdateModel(part, Prefix, null, null);

            var postedFile = ((Controller)updater).Request.Files["Shows_FileUpload"];
            if (postedFile != null && !String.IsNullOrEmpty(postedFile.FileName)) {
                _showService.SaveImageFile(part, postedFile);
                updater.TranscribeValidationDictionaryErrors<ShowServiceValidationKey>(_showService.ValidationDictionary);
            }

            return Editor(part, shapeHelper);
        }

Coordinator
Jul 11, 2012 at 5:56 PM

It is meant to build the editor shape, so you are misusing it here: you're throwing the shape away and possibly triggering all kinds of side-effects. What exactly are you trying to do?

By the way, even that driver has some really weird code: that cast of updater to Controller just to get Request... The request can be acquired through the work context.

Jul 11, 2012 at 7:59 PM

I had copied the driver code over from the Avatars module. I changed the cast of updater to:

var postedFile = _orchardServices.WorkContext.HttpContext.Request.Files["Shows_FileUpload"];

All I'm really trying to do is display and submit a form on the front end using my own Routes so that /Shows displays all the shows and /Show/Create gives me the edit form.

I put this together using the Forums module and Avatars module as examples. Is there a better one I can use. My only requirement is that this all needs to be done on the front end and not in Admin.

Thanks for the help...still trying to get all this down.

Coordinator
Jul 12, 2012 at 6:13 PM

It's quite hard to do your debugging in this way, with only a partial view of your code. I think you'll have to try harder with the debugger.