How to prevent Sitecore modal dialog from redirecting user to new item? - sitecore

I’m stuck with a little development annoyance. I have crated an application to extend Sitecore authoring interface.
The application queries some internal services and asks few additional questions from a content author before creating an new content item in Sitecore.
I have modeled the app on (Sitecore.Shell.Applications.Templates.CreateTemplate.CreateTemplateForm).
The problem I’m having is, as soon as an item is created my WizardForm is reloaded to load newly created item.
What I want is for the wizard to go through to the “Final” page and reload the main UI once the modal dialog is closed.
Exactly how new OOTB template wizard works. I know that if I comment my item creation code out the UI behaves as expected.
Looks like a create of an item generates some events in the background that UI is responding to and reloads my modal dialog with the newly created item. (I have tried the following solutions http://sdn.sitecore.net/Forum/ShowPost.aspx?postid=29092, http://sdn.sitecore.net/Forum/ShowPost.aspx?postid=29968, however this does not seem to solve it for me).
The original code seems to disable events like so:
this.CreateTemplatePicker.DisableEvents();
TemplateItem templateItem = Client.ContentDatabase.Templates.CreateTemplate(this.TemplateName.Value, selectionItem);
this.CreateTemplatePicker.EnableEvents();
I have tried the following:
Client.Site.Notifications.Disabled = true;
var item = container.Add(ItemUtil.ProposeValidItemName(this.Title.Value), Settings.ProductImageTemplateID);
Client.Site.Notifications.Disabled = false;
AND OLSO
Item item;
using (new EventDisabler())
{
item = container.Add(ItemUtil.ProposeValidItemName(this.Title.Value), Settings.ProductImageTemplateID);
}
All with the same result. The wizard modal dialog is reloaded as soon as I get to the page where the item is created.
Using fiddler I can see the command to reload the windows is sent to the client. I just cant figure out how do I tell Sitecore UI to ignore the event(s) or alternatively prevent event(s) from being generated in the first place. The first command being sent to the UI below tells the page to load Content Editor, exactly the thing I'm trying to prevent.
{"commands":[
{"command":"SetLocation","value":"/sitecore/shell/sitecore/content/Applications/Content%20Editor.aspx?fo=%7b186F686E-A8FF-4303-B59F-4D284A5A0196%7d&db=master&id=%7B186F686E-A8FF-4303-B59F-4D284A5A0196%7D&la=en&vs=1"},
{"command":"SetDialogValue","value":"{186F686E-A8FF-4303-B59F-4D284A5A0196}"},
{"command":"SetStyle","value":"none","id":"Constraints","name":"display"},
{"command":"SetStyle","value":"","id":"LastPage","name":"display"},
{"command":"SetAttribute","value":true,"id":"NextButton","name":"disabled"},
{"command":"SetOuterHtml","value":"<button id=\"CancelButton\" class=\"scButton\" TabIndex=\"0\" onclick=\"javascript:return scForm.postEvent(this,event)\" onkeydown=\"javascript:scForm.handleKey(this, event, null, '32')\">Finish</button>","id":"CancelButton"},
{"command":"Focus","value":"CancelButton","scrollintoview":"0"},{"command":"Eval","value":"scUpdateWizardControls();"},
{"command":"SetAttribute","value":true,"id":"BackButton","name":"disabled"},{"command":"Eval","value":"scAlignWizardButtons()"}
]}
Just a little info about my Sitecore environment:
Sitecore started
Sitecore.NET 7.0. (rev. 130810)
C:\Inetpub\wwwroot\sc71\Website\bin\Sitecore.Client.dll (Sitecore CMS, Sitecore Client Application, 7.0 rev. 130810)
C:\Inetpub\wwwroot\sc71\Website\bin\Sitecore.Kernel.dll (Sitecore CMS, Sitecore CMS Kernel Library, 7.0 rev. 130810)
C:\Inetpub\wwwroot\sc71\Website\bin\Sitecore.Nexus.dll (Sitecore.Nexus)
Operating system Microsoft Windows NT 6.2.9200.0
Microsoft.NET version 4.0.30319.18051
Process id: 8040
Windows identity used by the process: NT AUTHORITY\NETWORK SERVICE. Impersonation: False
Managed pipeline mode: Integrated

In the end the problem only affected bucketable items. No need to disable event or anything, that was a red herring. However, in my case I am working with buckets and bucketable items, so I needed to get it fixed.
The offending code ended being Sitecore.Buckets.Commands.AddFromTemplateCommand(). Thanks to Sitecore support engineers for getting to the bottom of this. A proposed workaround, that worked for me is as follow. This have been reported to Sitecore development team and I guess will be resolved at some stage in a future release of Sitecore. The current (at the time of writing) version Sitecore.NET 7.0. (rev. 130810) is affected.
You will need to substitute existing implementation with your own (see code below). To replace existing implementation overwrite the following configuration /App_Config/Includes/Sitecore.Buckets.config file at /sitecore/databases/database[#id="master"]. I ended up creating a configuration patch file that looked like this.
Configuration:
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<databases>
<database id="master" singleInstance="true" type="Sitecore.Data.Database, Sitecore.Kernel">
<Engines.DataEngine.Commands.AddFromTemplatePrototype>
<obj patch:instead="obj[#type='Sitecore.Buckets.Commands.AddFromTemplateCommand, Sitecore.Buckets']"
type="Sitecore.Support.Buckets.Commands.AddFromTemplateCommand, MyAssembly"/>
</Engines.DataEngine.Commands.AddFromTemplatePrototype>
</database>
</databases>
</sitecore>
</configuration>
Code:
using Sitecore.Data.Items;
using Sitecore.Text;
using Sitecore.Web.UI.Sheer;
using System;
using System.Web;
namespace Sitecore.Support.Buckets.Commands
{
public class AddFromTemplateCommand : Sitecore.Buckets.Commands.AddFromTemplateCommand
{
protected override Sitecore.Data.Engines.DataCommands.AddFromTemplateCommand CreateInstance()
{
return new AddFromTemplateCommand();
}
protected override void SetLocation(Data.Items.Item item)
{
if ((HttpContext.Current != null) && (Context.ClientPage != null))
{
// This condition is set to go around an issue when a bucket item is created from within a custom wizard app.
// Replace the specified path with your own one.
if (Sitecore.Context.RawUrl != null && !Sitecore.Context.RawUrl.Contains("/sitecore/shell/Applications/Issues/Create Product Bucket.aspx"))
{
UrlString str = new UrlString(Sitecore.Buckets.Util.Constants.ContentEditorRawUrlAddress);
str.Add(Sitecore.Buckets.Util.Constants.OpenItemEditorQueryStringKeyName, item.ID.ToString());
item.Uri.AddToUrlString(str);
UIUtil.AddContentDatabaseParameter(str);
SheerResponse.SetLocation(str.ToString());
}
}
}
}
}

Related

Sitecore > Disable html caching of a rendering

I am trying to upgrade the old project(based on storefront for Sitecore 8.0) to version Sitecore 8.1(latest one at the moment).
During this process I faced with a problem when I see the same products for all categories. So, for example, I select a category first time and see correct products. After that I choose any other category, but still see the same products(from the first category).
These data are returned by ProductList rendering(a controller rendering) and it is not run after the first call anymore(tried to reach corresponding action in a CatalogController in debug mode).
If we clear all caches(..sitecore/admin/cache.aspx) - then it works again, but only first time.
I understand that I can't disable caching for the whole site, I need to do it for this generic page(with "*" in item name) where the commerce data are shown - so for all categories and product pages. I checked this rendering in design mode and can see that all checkboxes related to cache are unchecked at the moment. Don't know what I have missed.
Thank you in advance for the help.
I believe you have caching enabled on control/sublayout defintion level which will cause to cache that rendering on every page on the site, a while ago i was able to come up with a solution to disable caching for a specific rendering/sublayout on specific pages, while keeping it caching on other pages.
I basically created a new rendering parameter template with checkbox "Cancel Cache Settings", then in my rendering definition item, I set the parameter template to the new template, if your site runs on Sitecore MVC, do the following:
Create a class called 'SetCacheability'
namespace Sitecore.SharedResources.Pipelines.Rendering
{
public class SetCacheability : Sitecore.Mvc.Pipelines.Response.RenderRendering.SetCacheability
{
protected override bool IsCacheable(Sitecore.Mvc.Presentation.Rendering rendering, Sitecore.Mvc.Pipelines.Response.RenderRendering.RenderRenderingArgs args)
{
if (!String.IsNullOrEmpty(rendering.Parameters["Cancel Cache Settings"])
&& rendering.Parameters["Cancel Cache Settings"] == "1")
{
return false;
}
return base.IsCacheable(rendering, args);
}
}
}
Create the patch config file in your include folder:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<mvc.renderRendering>
<processor patch:instead="processor[#type='Sitecore.Mvc.Pipelines.Response.RenderRendering.SetCacheability, Sitecore.Mvc']"
type="Sitecore.SharedResources.Pipelines.Rendering.SetCacheability, [YOUR ASSEMBLY NAME]"/>
</mvc.renderRendering>
</pipelines>
</sitecore>
</configuration>
Here is the blog i wrote on this: http://www.sitecorecoding.com/2014/09/disabling-caching-for-rendering-on-some.html
Hope this helps
The caching settings you've disabled are located in the presentation details on the control level:
Additionally, you should ensure that caching is disabled on your sublayout (or rendering) definition (under /sitecore/Layout/Sublayouts):

Sitecore 8 throwing null exception when Datasource item does not exist

I am facing the below problem and can't find out how to get around to it.
I have a rendering and its datasource is pointing to an item in the tree.
I publish the rendering but I don't publish the referenced item.
While viewing the page I get an error. [attached is the screen shot of the error i am getting]
I am using glass mapper.
Thanks in advance for your help.
The best solution would be to tap into the mvc.getRenderer pipeline and validate the datasource actually exists, otherwise fallback gracefully.
There are a number of solutions already proposed for this and is a known "issue", although it is not a Glass specific issue:
Sitecore - automated validation of MVC rendering DataSource
Rendering Exception Handling - The Right Way
Robust MVC Rendering Exception Handler
HANDLING RENDERING EXCEPTIONS IN MVC SOLUTIONS WITH THE SITECORE ASP.NET CMS
All these solutions check if the datasource item exists, in normal mode the error is swallowed but in Page Editor mode a warning is displayed to the editors so show the fact that datasource needs to be set in cases where none has been selected.
It was also raised as an issue in Glass Mapper with a similar solution.
To elaborate jammykam's answer, you can do something like the code below which I have found in this blog
Config Patch:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<mvc .getrenderer="">
<processor patch:instead="processor[#type='Sitecore.Mvc.Pipelines.Response.GetRenderer.GetViewRenderer, Sitecore.Mvc']" type="Namespace.To.Sitecore.Pipelines.Response.GetRenderer.GetViewRendererWithItemValidation, Library">
</processor>
</mvc>
</pipelines>
</sitecore>
</configuration>
Code:
public class GetViewRendererWithItemValidation : GetViewRenderer
{ `
protected override Renderer GetRenderer(Rendering rendering, GetRendererArgs args)
{
var viewRenderer = base.GetRenderer(rendering, args) as ViewRenderer;
if (viewRenderer == null)
return null;
// Ignore item check when in page editor
// Also this will break if the item for the datasource has been deleted without removing the link.
if (Context.PageMode.IsPageEditor || Context.PageMode.IsPageEditorEditing)
return viewRenderer;
// Override renderer to null when there is an unpublished item refererenced by underlying view
return viewRenderer.Rendering.Item != null && viewRenderer.Rendering.RenderingItem.InnerItem != null
? viewRenderer
: null;
}
}

Can I guarantee publishing of an item I've just created?

I've got a situation where I want end-users to be able to create new Sitecore items... and then immediately be able to navigate to the Item's URL. Obviously the item will have to be published... but this happens in something of a black box. Is there a way to guarantee an item has been published from Master to Web?
Alternately, I could create the Item in the Web DB... then re-create/copy a Master version at some point. But this strategy seems fraught with peril. And maybe just a bad idea in general.
Suggestions? Am I being needlessly paranoid about creating items directly in Web?
I'll start by saying my answer is not necessarily advisable. However, depending on your situation it should work.
If the items that users are creating always have the same template you could try creating a custom item resolver that falls back to using the Master database.
Allow Sitecore to attempt to resolve the item normally.
When it fails, look in the Master database.
If the item is in there, make sure it has the correct template
Set the found item as the context item.
Using this method, you can publish the items from Master->Web s normal, but you don't have to wait until publishing is completed to see it.
Once again, this should solve the problem, but it's up to you to weigh the risks of serving up Master DB content.
You can't "guarantee" because publishing may be queued and won't go live instantly. Instead if you need instant access, I recommend a preview site that points to the master database:
How to Setup a Sitecore Preview Site to Review Content Before Publishing
You could create a event mapping to a class with the code to publish the item. In the code you can define any rule you want regarnding whether to publish or not.
<event name="item:saved">
<handler type="YourType.ItemEventHandler, YourType" method="OnItemSaved" />
</event>
And the OnItemSaved would look like this: (not tested)
protected void OnItemSaved(object sender, EventArgs args)
{
if (args == null)
{
return;
}
Item item = Event.ExtractParameter(args, 0) as Item;
var webDb = Sitecore.Configuration.Factory.GetDatabase("web");
var masterDb = Sitecore.Configuration.Factory.GetDatabase("master");
foreach (Language language in masterDb.Languages)
{
var options = new PublishOptions(masterDb, webDb, PublishMode.SingleItem, language, DateTime.Now)
{ RootItem = item, Deep = true };
var myPublisher = new Publisher(options);
myPublisher.Publish();
}
}
And about creating the item in the web db, I also wouldn`t go that way. It would be replaced by the default publishing process unexpectedly.
Personally i don't like front-end user creating items in master database, i feel only content authors/editors should be the ones who create items from either content editor or page editor.
There is no guarantee the item gets published instantly, I would recommend you to store any user-created data in a separate database and on the redirect URL just read the data from this database.

Sitecore file download works only for admin user

I have a Sitecore 6.4 setup where an editor can click a button to generate a Word doc. I was adding the file to the media library and that was working fine. The editor would click the button in the content editor, the file was generated, media item was generated, then the content editor would show the new item in the media library and the editor could click the "download" button on the ribbon or the item to download it. However, my media library was getting unnecessarily filled up so I am trying to bypass the media library.
Instead of making the file in an arbitrary location as before, I am putting it in the temp directory like this:
wordOutputPath = Sitecore.IO.FileUtil.GetWorkFilename(Sitecore.Configuration.Settings.TempFolderPath, printItem.Name, ".docx");
File.Copy(wordTemplatePath, wordOutputPath);
WordprocessingDocument doc = WordprocessingDocument.Open(wordOutputPath, true);
After I "fill in" the file with content, I do this:
Sitecore.Context.ClientPage.ClientResponse.Download(wordFilePath);
Now if I am logged in as a Sitecore admin I get the browser's download dialog and can download the file. However, if I am logged in as a non-admin user, I get a little clicking and whirring, so to speak, and the file is generated, but the save file dialog never comes up in the browser. I can go in through the file system and see & open the Word doc, and it looks fine.
I found something in the Sitecore release notes for 6.6:
Released Sitecore CMS and DMS 6.6.0 rev. 130111 (6.6.0 Update-3)
[...]
Miscellaneous
Only Administrators were allowed to download files. (316774, 348557)
This was a problem in several areas of the system, for example the Package Generator and the Export Language Wizard in the CMS. It also affected the Export Users Wizard in the ECM module.
So I tried using SecurityDisabler (no longer have the code handy) and UserSwitcher like this:
using (new Sitecore.Security.Accounts.UserSwitcher(Sitecore.Security.Accounts.User.FromName("sitecore\admin", false)))
{
Sitecore.Context.ClientPage.ClientResponse.Download(wordFilePath);
}
The IUSR and IIS_IUSRS accounts both have read, list & read/execute permissions on the temp folder and the individual files show read & read/execute permissions for those two accounts as well.
What can I do to allow non-admin users to download these files? Does the bug that was fixed in 6.6 have anything to do with it?
Thank you.
Calling Sitecore.Context.ClientPage.ClientResponse.Download() method will result in a Download command sent back to the Sitecore Client in the Web browser, which in turn will make a call back to the server to execute it. This will be why using a security switcher (or a security disabler for that matter) before calling the Sitecore.Context.ClientPage.ClientResponse.Download() method has no effect, as the admin only restriction is being applied to the download else where.
If you search through sitecore\shell directory in your webroot, you will find that there is a file called download.aspx. This should be the actual page the download request is sent to. This web form page inherits the Sitecore.Shell.DownloadPage class, so if you take a look at that class's implementation, you will find that the OnLoad method is implemented as such:
protected override void OnLoad(EventArgs e)
{
Assert.ArgumentNotNull(e, "e");
base.OnLoad(e);
string fileHandle = StringUtil.GetString(new string[] { base.Request.QueryString["file"] });
bool flag = false;
string filename = FileHandle.GetFilename(fileHandle);
if (!string.IsNullOrEmpty(filename))
{
fileHandle = filename;
flag = true;
}
if (!string.IsNullOrEmpty(fileHandle))
{
if (MediaManager.IsMediaUrl(fileHandle))
{
this.DownloadMediaFile(fileHandle);
}
else if (fileHandle.IndexOf("id=", StringComparison.OrdinalIgnoreCase) >= 0)
{
this.DownloadMediaById(fileHandle);
}
else if (flag || Context.IsAdministrator)
{
this.DownloadFile(fileHandle);
}
}
}
As you can see, the last if block has an IsAdministrator check which would be where your non-admin users are getting blocked.
Although I haven't 100% verified that this is where the problem lies, the download restriction issue can be resolved, by creating a new class that inherits Sitecore.Shell.DownloadPage and overrides the OnLoad implementation with some extra security checks as required. I recommend that you don't remove the IsAdministrator check altogether as it is there for a good reason. Update the Inherits attribute in the download.aspx to make it use it instead.

Umbraco Document.getProperty(...).Value throws Null Reference Exception

I am writing a small app that links into Umbraco (a small stand-alone console application that will eventually run as a scheduled task on the server) and I'm using the Umbraco APIs (4.5.2) to make changes to the database/document.
Here is a fragment of what I'm doing:
IEnumerable<Document> documents = Document.GetChildrenForTree(parentDocumentId);
foreach (Document doc in documents.Where(d => d.Published))
{
doc.getProperty("myData").Value = "some data"; // Exception here
// ...other stuff here...
}
However I always get a NullReferenceException because there are no properties. This confuses me because I can see that there are 5 properties in the umbraco interface.
A colleague suggested that I use a Node instead of a document, however I can't even create one as I get a NullReferenceException from the Node class constructor.
Node myNode = new Node(-1); // NullReferenceException here
Does anyone have any ideas?
The document class gets/sets information from the umbraco database. Since your running code in an out of band console application it can't find the umbraco context. Therefore throwing a null reference exception.
You need to run the code inside of the umbraco process. There is a asmx webservice that exists for third party integration. /umbraco/webservices/api/documentservice.asmx
Another way of achieving this could be to use linq2umbraco.
for further details see http://our.umbraco.org/forum/core/41-feedback/7699-UmbracoLinq-in-console-app--Having-some-troubles
I checked out the 4.5.2 source recently, to find that populating Document and Node objects only requires a connection using umbracoDbDsn. So if you have an AppSetting called umbracoDbDsn which points to a valid Umbraco database instance, you'll be good.
HTH,
Benjamin