Saving part when using a Controller.

Topics: Core, Customizing Orchard
Jul 17, 2013 at 8:20 PM
Edited Jul 17, 2013 at 8:21 PM
I am trying to put my settings page on the admin menu rather than just on the General Settings page, because I think that is cleaner. It looks like I need to have a controller and a view in order to do this, as opposed to a driver and a view as I would if I were just putting it onto the General Settings page.

I can get the page to show and take in the data, but I don't know how to make the data save. I'm using a viewmodel for interfacing with the view, and, when using a driver, it just magically saves. I don't see in the other modules (like Orchard.Warmup and Orchard.Users) where the data gets saved, either.

Here's the controller code that I'm using. Any direction would be greatly appreciated!
    public class AdminController : Controller, IUpdateModel
    {

        #region " Private Properties "

        private IOrchardServices Services { get; set; }
        private ISignals Signals { get; set; }
        private Localizer T { get; set; }

        #endregion

        #region " Constructor "

        public AdminController(IOrchardServices services, ISignals signals)
        {
            Services = services;
            Signals = signals;
            T = NullLocalizer.Instance;
        }

        #endregion

        #region " Index "

        public ActionResult Index()
        {
            // Verify permissions.
            if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings")))
            {
                return new HttpUnauthorizedResult();
            }

            // Get model.
            var part = Services.WorkContext.CurrentSite.As<LoggingSettingsPart>();

            // Create viewmodel.
            var model = new LoggingSettingsPartViewModel()
            {
                ServiceUrl = part.ServiceUrl
            };

            // Return viewmodel.
            return View(model);
        }

        #endregion

        #region " IndexPost "

        [HttpPost, ActionName("Index")]
        [FormValueRequired("submit")]
        public ActionResult IndexPost()
        {
            // Verify permissions.
            if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings")))
            {
                return new HttpUnauthorizedResult();
            }

            // NOTE:  Adding a model error will prevent the model from being saved to the database.
            //        Thus, any model errors will mean that we have not made a change.
            bool changed = true;

            // Get model data.
            var viewmodel = new LoggingSettingsPartViewModel();
            if (!TryUpdateModel(viewmodel))
            {
                // Alert if unable.  (Not sure why this would ever fail.)
                AddModelError("LoggingSettingsUpdate", T("Unable to update logging settings."));
            }
            else
            {
                //AddModelError("Temp", T("Temp"));
            }

            // Signal that settings have changed.
            // (Clears the cached settings.)
            if (changed)
            {

                Signals.Trigger("MyModule.NewLogging.SettingsChanged");
            }

            // Return the view.
            return Index();
        }

        #endregion

        #region " AddModelError "

        private void AddModelError(string key, LocalizedString errorMessage)
        {
            ModelState.AddModelError(key, errorMessage.ToString());
        }

        #endregion

        #region " IUpdateModel Members "

        bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties)
        {
            return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
        }

        void IUpdateModel.AddModelError(string key, LocalizedString errorMessage)
        {
            AddModelError(key, errorMessage);
        }

        #endregion

    }
Coordinator
Jul 17, 2013 at 10:22 PM
You generally don't have to explicitly save, except when you created a new record, or similar circumstances. If there is no exception within the transaction scope for the request, the changes will be automatically tracked and persisted. Does this answer your question?
Jul 18, 2013 at 1:19 PM
Edited Jul 18, 2013 at 1:24 PM
That's not the behavior that I'm seeing. I can make a change and it will stay on the response, but it doesn't make it into the database and it doesn't appear when I load the page again. I think the viewmodel is being updated but not the model (which makes sense, since I'm running the update on the viewmodel). However, the other modules at which I'm looking (Users, Warmup) do the same thing..

[It DOES just save when using a driver, but, there, I get the actual Part passed in and do the TryUpdateModel on that.]
Jul 18, 2013 at 1:43 PM
Edited Jul 18, 2013 at 2:04 PM
I can get it to work by grabbing the part like this and doing the update:
        [HttpPost, ActionName("Index")]
        [FormValueRequired("submit")]
        public ActionResult IndexPost()
        {
            // Verify permissions.
            if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings")))
            {
                return new HttpUnauthorizedResult();
            }

            // NOTE:  Adding a model error will prevent the model from being saved to the database.
            //        Thus, any model errors will mean that we have not made a change.
            bool changed = true;

            // Get model data.
            var part = Services.WorkContext.CurrentSite.As<LoggingSettingsPart>();
            if (!TryUpdateModel(part))
            {
                // Alert if unable.  (Not sure why this would ever fail.)
                AddModelError("LoggingSettingsUpdate", T("Unable to update logging settings."));
            }
            else
            {
                //AddModelError("Temp", T("Temp"));
            }

            // Signal that settings have changed.
            // (Clears the cached settings.)
            if (changed)
            {

                Signals.Trigger("BBTSec.BrokerageLogging.SettingsChanged");
            }

            // Return the view.
            var viewmodel = new LoggingSettingsPartViewModel()
            {
                ServiceUrl = part.ServiceUrl
            };
            return View("Index", viewmodel);
        }
This is not how the other modules do it, but it works..

EDIT: Except that it saves regardless of whether or not I add a model error. I guess I can keep a copy and set it back if anything, but I wasn't expecting as much manual management. (But, if it works, it works.)

EDIT2: Or I can just save it off if it validates.
        [HttpPost, ActionName("Index")]
        [FormValueRequired("submit")]
        public ActionResult IndexPost()
        {
            // Verify permissions.
            if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings")))
            {
                return new HttpUnauthorizedResult();
            }

            // Assume we will run into an error.
            // If we succeed, we'll change this value.
            bool changed = false;

            // Get model data.
            var viewmodel = new LoggingSettingsPartViewModel();
            if (!TryUpdateModel(viewmodel))
            {
                // Alert if unable.  (Not sure why this would ever fail.)
                AddModelError("LoggingSettingsUpdate", T("Unable to update logging settings."));
            }
            else
            {
                changed = true;
            }

            if (changed)
            {
                // We successfully updated the viewmodel, so persist the new settings.
                var part = Services.WorkContext.CurrentSite.As<LoggingSettingsPart>();
                part.ServiceUrl = viewmodel.ServiceUrl;

                // Signal that settings have changed.
                // (Clears the cached settings.)
                Signals.Trigger("BBTSec.BrokerageLogging.SettingsChanged");
            }

            // Return the view.
            return View("Index", viewmodel);
        }
Coordinator
Jul 19, 2013 at 1:03 AM
You almost figured it out I think. Model errors are not exceptions. But yes, it's your responsibility to only change the records if validation was successful.