CET 8.1: Deletions Audit

Update 8.1 of the Content Export Tool adds a new feature in the Special Audits section, the Deletions Audit:

deletions audit

The Deletions Audit is a simple audit that reports the items in the Recycle Bin, providing each item’s path, ID, and date deleted.

output

The main benefit of the Deletions Audit is that it serves as a way to “package” deletions to promote them to another environment.

For example, suppose you have made a lot of content changes in a testing environment which involves deleting obsolete items and making sure nothing breaks in the test environment. Now you need to delete all the same items on production, but you’ve deleted a whole bunch of items and don’t remember exactly what was changed.

Using the Deletions Audit, you can get a list of all of the items in the Recycle Bin that were deleted by you (“My Deleted Items” option) within the date range when you were making your changes. Review the CSV and make sure to remove any line items that aren’t actually related to the changes you intend to promote. Then, on prod, upload Deletions Export.csv in the Import section, select the Delete option, and run the import.

deletions import

This will delete each item in the CSV based on the Item Path column; if the item does not exist in that environment, that line will be skipped but the Deletion Import will continue to the next line.

Version 8.1 can be downloaded on Github.

SEO friendly URL masking for bucketed Sitecore items – making a custom item resolver and link provider

Item buckets allow for a large number of items to be stored in the Sitecore content tree, and many modules, such as WeBlog, implement bucketing to store content in a logically organized manner – for example, by automatically placing them into subfolders by year and date (e.g. /blog/2020/1/new-blog-post). This automatic organization is helpful for finding content, and the bucketing helps alleviate performance issues that occur with a huge amount of content, but the resulting URLs are not great, for the following reasons:

  1. They are not very SEO friendly
  2. They are not very user friendly – users cannot easily remember or guess a URL that includes date-based segments like “/blog/2020/1/new-blog-post”, as opposed to intuitive URLs like “/blog/new-blog-post”
  3. Although a page that is months or years old may still be highly relevant, having the URL reveal that it is old may make the users think that it is stale content. Hiding the date segments from the URL will help avoid bias against old content.

There are two main steps to implementing friendly URL masking. For the purpose of this article I will discuss masking items with the Article taxonomy under /content/articles, but keep in mind that you can implement URL masking for any type of content and under any root path. However, the broader your masking — that is, if you choose not to filter by a Template or Taxonomy and/or choose not to restrict masking to a particular folder — you are more likely to run into problems with your Item Resolver returning the wrong item.

1. Custom Item Resolver – Resolve friendly URLs to the matching Sitecore item

The first step is to create a custom item resolver. This is a HttpRequestProcessor that is patched after the Sitecore ItemResolver Pipeline. The goal of our custom item resolver is, if no item has already been found at the requested path, to find the item (using Sitecore search) whose name matches the last part of the URL.

item resolver.PNG

Let’s dissect the code above.

First, if Sitecore.Context.Item is not null, we exit out of the custom processor.

Next, we take a look at the requested URL. In my code, I am only trying to mask content under /content/articles, so if the requested path does not start with that path, I exit the processor.

I then get the requested item name, which is the last part of the path. I replace all hyphens with spaces and transform it to lowercase.

Finally, I use Sitecore search to find results where the Content Type taxonomy (“contenttype” in my Index) equals “Article” and where “searchname” equals the transformed item name.

customsearchresult.PNG

But wait, what’s searchname? Because I did not want to have to worry about case sensitivity or items where the item name actually contains hyphens, I made a computed field called “searchname”. The computed field simple takes the item name, replaces all hyphens with spaces, and converts to lowercase. I did this because Sitecore search does not allow me to do transformations within the Query predicate — that is, this line below throws an error:

var query = searchContext.GetQueryable<CustomSearchResult>().Where(x => x.ContentType == "Article" && x.Name.ToLower().Replace("-", " ") == articleName);

Therefore, my string transformations have be done when the item is indexed, and I search against the computed field.

computed field.PNG

2. Custom Link Provider – generate friendly links to blog pages within the site

With the custom item resolver, /content/articles/seo-friendly-urls will resolve to the item /content/articles/2020/january/seo-friendly-urls, but the internal links within our site that point to our Seo Friendly Urls article (e.g. URLs on the search results page, URLs generated by Link fields) will still appear as /content/articles/2020/january/seo-friendly-urls and will resolve to the full URL — the custom item resolver takes friendly URLs and finds the associated content item, but does nothing to change unfriendly URLs (remember, if Sitecore.Context.Item is not null, the item resolver does nothing).

To make sure all of the links to the article pages within your site use the friendly URL as well, we need to create a custom link provider.

custom link provider.PNG

In the code above, you can see that if the current item is NOT an article under our “/content/articles” path, we use the base Sitecore method to get the item URL. However, if we have an article, we take the item name and generate a URL that correlates to our masked URLs from step one (notice how inversely to step one, in this step we replace spaces with hyphens to create a friendly URL that our custom item resolver can process).

The last step is to patch in your custom item resolver and link provider:

patch.PNG

That’s it! Your link provider will generate your item links as friendly URLs, and your item resolver will resolve those friendly URLs into the associated item! One thing to note – you may have caught this, but the item resolver will fail if multiple Article items under /content/articles have the same name. It uses Sitecore search to get the first Article item whose name matches, so if there is more than one item that matches, that’s a problem. Keep that in mind when naming your items if you are using or plan to implement URL masking.

Accelerating WFFM-to-Sitecore-Forms Migrations with the WFFM Conversion Tool and Content Export Tool

Sitecore Web Forms For Marketers (WFFM) was officially deprecated with the release of Sitecore XP 9.1, replaced by Sitecore Forms. While this was no issue for brand new website development, this was a huge pain point for Sitecore users who currently had an 8.2 or lower site and wanted to upgrade to Sitecore 9. Sitecore offered no out-of-the-box solution for migrating WFFM to Sitecore Forms, so the only option was to manually recreate all WFFM forms as Sitecore Forms. Now, I have a few clients whose websites are largely form-driven and as such, they have hundreds of forms, and manually recreating every one posed a tedious and time-consuming task. Fortunately, there are two modules that now exist that can make form migrations a breeze: the WFFM Conversion Tool and the Content Export Tool. I’ll explain what each does, and how we used the two together to drastically accelerate a site migration.

The WFFM Conversion Tool, created by Alessandro Faniuolo, is an open source console application that automatically converts WFFM forms in your old solution into Sitecore Forms in your new solution, and migrates the form data as well so that previously collected data is not lost. I won’t go into extensive detail here, but you can read more about it on his blog. The important thing to know is that is that if you’re upgrading to Sitecore 9.1+ and need to migrate forms, this is the tool to use.

However, your new Sitecore 9 forms may not be quite finished yet, and this is where the Content Export Tool comes into play. If you’re converting WFFM to Sitecore 9 forms, there may be some style or formatting changes you need to make, such as adding new CSS classes or making structural changes such as wrapping field items in form sections.

The Content Export Tool’s Import feature can be used to modify the CSS classes on hundreds of form items in a matter of minutes. You can read the full details on content importing here, but here’s the short version of how to modify the fields in bulk:

  1. Run an Export to get all form items with the Css Class field. This is a very simple export: just set Start Item to “/sitecore/Forms” and Fields to “Css Class”
  2. Open up the exported CSV file and fill out the Css Class column to set the class on every item you need. You can delete line items you don’t need to update, but you can also just ignore them — if the Css Class isn’t changed, then no change will be made in Sitecore, so there’s no risk to including lines that haven’t been modified.
  3. Run the Content Import with Update selected.
    import css.PNG
  4. Publish your forms — or, if you checked off “Publish changes” before you ran the import, you’re already done.

As you can see, setting Css Classes in bulk is super easy. Making structural changes is a bit more complicated. For this example, we need to wrap one specific form field, the Name input field, in a Section.

  1. Run a content export to get all of the Input field items under /sitecore/Forms, with ALL of their field values:
    1. Start Path: /sitecore/Forms
    2. Template: /sitecore/templates/System/Forms/Fields/Input
    3. All Fields: true

      output
      An example of the exported CSV data
  2. Now you need to do some Excel work. Modify your CSV file to get rid of all of the line items you don’t want. In this case, I want to wrap every Name field on every form in a section, but I don’t want to modify anything else, so I’m going to modify my CSV to get rid of every line where Title does not equal “Name”. I did this by sorting on the Title column A-Z, and then deleting all lines above and below those where Title = Name, but I’m not an Excel expert so there are probably easier ways to do this.

    filtered.PNG
    Modified CSV. In this example I don’t have a ton of Name items, but let’s pretend there are a lot more lines here.
  3. Create a new CSV file.
  4. Copy the Item Path column from the export to the blank CSV.
    item path.PNG
  5. On every row, remove the last part of the item path (e.g. “/sitecore/Forms/SignUp/Page/Name” > “/sitecore/Forms/SignUp/Page”
    updated path.PNG
  6. Add the Template and Name columns and fill them out. Template will be “/sitecore/templates/System/Forms/Fields/Section” but Name can be anything
    template and name.PNG
  7.  Now you’re ready to run the Import. Save and upload the new CSV and select Create.
  8. Now we’re going to create another new file. This time, copy all of the contents of the original file (we still want to keep our original file intact for our final step).
  9. Modify the Item Path on every line – we’re going to replace the last part of the path, which is currently our Field item name, with the new Section item name.
    updated paths.PNG
  10. Add the Template and Name columns. Template is “/sitecore/templates/System/Forms/Fields/Input”; name can be anything, but I’m just going to call them all “Name”
    template and name new.PNG
  11. Run the import. We’re using Create again. This will create all of our new Name Input items under our new Section items, and will populate all of the field values to match the original Name input items.
  12. Lastly, we need to delete the old Name input items. This is why we kept our original export CSV unchanged – because it already has the full list of item paths we need to delete. If you modified this file directly and changed the paths to do the second import, that’s ok, you just need to repeat steps 1 and 2 to recreate the CSV with all the original items paths, or modify the paths again to set them back to the original paths.

    Upload your CSV with all of the original item paths, select Delete, and run the import
    delete.PNG

  13. Review your changes and publish your forms.

The limitation of this process is that you can’t control where under the parent the Section item gets created, and if you have multiple field items, it’s likely that the Section won’t be placed in the same position where the Name field was (it will likely either be created as the last item under its parent, or put in alphabetical order). So, you’ll want to go through your forms and move the new Section item to the correct position as needed.

Here’s a testimonial from my colleague Matt Richardson, who used the processes above to save a ton of time and work in a site upgrade — he’s the one who figured out how to use the tool for the item-wrapping process and shared his method with me.

I used the Content Export Tool to update field values quickly on over 80 Sitecore forms and their sub-items. Multiple different value changes needed to be made to hundreds of fields over more than 80 forms. The Content Export Tool was exceptionally useful in this scenario because a specific set of fields and their values, tied to the value of another field on the item, needed to be updated where updating Standard Values would’ve updated all of them. We were looking to apply CSS classes to this specific subset of Forms items. I was able to use the filters available on through the tool to target just the items I needed to change, export just the field values I needed to change, update the values quickly in the CSV, and then upload the updated CSV to update the field values for all those items. Doing the change by hand would’ve required at least several hours and greatly increased the risk of a mistake. I subsequently used the tool to make structure changes to the forms. We had a specific Form field type that needed to be wrapped in a Forms Section. I used the tool to create the new sections (create), create duplicate items under the new section (move), and remove the old items (delete).

The WFFM Conversion Tool and the Content Export Tool are both available on the Sitecore Marketplace and Github. If you’ve found them helpful, please rate them on the Marketplace; if you found a creative way to use the Content Export Tool that I haven’t previously discussed, please share it with me!

WFFM Conversion Tool:
Github
Sitecore Marketplace

Content Export Tool:
Github
Sitecore Marketplace

Setting up a Sitecore RSS Feed – things you might have missed

The Sitecore documentation provides straightforward instructions on how to set up an RSS feed. It guides you through creating your RSS feed item and configuring field mappings so that items can be listed in the feed. This is everything you need to get it working – theoretically – but there are a few additional steps you may need to take once you get to your prod environment.

Allowing anonymous access to the /sitecore/feeds folder

After you publish your RSS feed item and browse to the feed, you may encounter this page:

access denied.png

The Sitecore RSS feed mechanism is stored in the /sitecore/shell/applications/feeds and sitecore/shell/Feeds folders, and if anonymous users are denied access to /sitecore/shell (which they should be!), they won’t be able to access these folders and will get this error. You may have missed this when testing it locally because if you are not testing in a CD/CM environment, you were probably still logged in as an authenticated user when you browsed to the feed.

To allow access, add this to your web.config:

<location path="sitecore/shell/feeds">
  <system.web>
    <authorization>
      <allow users="?" />
    </authorization>
  </system.web>
</location>
<location path="sitecore/shell/applications/feeds">
  <system.web>
    <authorization>
      <allow users="?" />
    </authorization>
  </system.web>
</location>

There are no fields available to map for the Title

no titles

The RSS feed only supports plain text fields, e.g. Single-Line Text or Multiline text. If your title fields are Rich Text, they will not be available to map to the Title field. You will need to either change these fields to single or multiline text (which could be risky, as if any of the existing fields contain HTML, this HTML will break the RSS feed); or create a new plain-text title field.

Note: If you choose to create a new plain-text title field, but are concerned about having to fill it out for hundreds of items, you can use the Content Export Tool to export all of the rich-text title field values, strip out any HTML and change the field name to the new plain-text field, and upload to instantly populate the new title field on every item.

CET 8.0 – Modifying Sitecore Rendering Parameters with the Content Export Tool

Recently I have had to write a few import scripts to do bulk modification to rendering parameters, so I have added the capability to the Content Export Tool. Below I will explain the new Rendering Parameters Import feature; explain the two scenarios in which I needed to modify rendering parameters in bulk; and how each scenario is accomplished with the Content Export Tool.

It is recommended that the Rendering Parameters Import is used by developers rather than content authors, as it requires a somewhat more technical understanding of Sitecore than the Content Import.

rendering parameters import

How to use the Rendering Parameters Import

As with Content Import, this import is done using a CSV file. The Rendering Parameters Import can do the following:

  • Change the placeholder of a rendering
  • Change the value of a rendering parameter field on a specific rendering
  • Change the position of a rendering (relative to all other renderings, or relative to the placeholder it is in)
  • Move the rendering before or after another rendering

The above functions are accomplished using the following CSV headers:

  • Item Path This column should contain the item path or ID. 
  • Apply to All Subitems Apply the changes on this line to the item and all subitems, defaults to false (TRUE/FALSE)
  • Template With Apply to All Subitems, apply the changes only to items with the specified template name or ID. This field is ignored when Apply to All Subitems is not true
  • Component Name The name or ID of the component to modify. If this is not specified, no changes will be made
  • When Placeholder Equals Modify a component within this particular placeholder
  • Nth of Type Modify the Nth component with the specified name (NUMERIC, STARTS AT 1)
    • With When Placeholder Equals, modify the Nth component within the specified placeholder with the specified name
  • Parameter Name The name of the rendering parameter to modify or add
  • Value The value to set for the rendering parameter
  • Placeholder The placeholder to move the rendering to
  • Position The position to put the rendering in relative to all other renderings (NUMERIC, STARTS AT 0)
  • Position in Placeholder The position to put the rendering in relative to its placeholder (NUMERIC, STARTS AT 0)
  • Before The name of the FIRST rendering to put this rendering before
  • After The name of the LAST rendering to put this rendering after

By default changes will be made to the first rendering that matches the requirements (i.e. matches Component Name and optional When Placeholder Equals). However, if you wish to target a rendering other than the first, you can use the Nth of Type header. For example, if you have three instances of the Rich Text component in the MainContent placeholder, you can target the second one by setting When Placeholder Equals to “MainContent” and Nth of Type to “2” (note: Nth of Type starts at 1, but Position and Position in Placeholder start at 0).

As with the Content Import, you can automatically publish all modified items by checking off the Publish Changes checkbox.

Example 1: Change the value of a rendering parameter

The first issue that occurred was when a client had me add a new rendering parameter field to a dynamic content listing component. The new field was a checkbox field called “Show Subtitle”. After this was created, they needed help because they needed “Show Subtitle” to be checked off on hundreds of pages… but not on EVERY instance of the component, only on the pages in a specific folder. So, I couldn’t easily check off “Show Subtitle” for every instance with the Standard Values; I had to modify them programatically, or else deal with a very tedious manual process.

How to do it:

This import only requires on line in the CSV, with the following values:

  1. Item Path: /sitecore/content/home/NewsListings (the folder where I needed to modify all items)
  2. Apply to Subitems: TRUE
  3. Template: News Listing Page (only modify pages of the particular template)
  4. Component Name: Dynamic Content Listing
  5. Parameter Name: Show Subtitle
  6. Value: 1

CSV

I uploaded the file and checked Publish Changes so that I could publish all of the modified pages without manually publishing the root folder, which would risk publishing changes that weren’t intended to be published yet.

Example 2: Change the placeholder of a rendering

The second issue was that the client had hundreds of pages of a particular template (Article Page) on their site, and they decided that they didn’t like the position of a particular component (the metadata component, which showed the date published and the author). They wanted it moved from the side of the page to the top content, beneath the Page Header and above the Social Share Bar.

This couldn’t be done with a simple CSS or HTML code change, because the metadata was a component on the page and the place it showed up was based on its placeholder (although technically it could maybe have been moved with CSS, it would have been hacky and not the right way to go about fixing it). So, I needed to update the placeholder and make sure it came before the Social Share Bar. Once again, with hundreds of pages to update, this would have been extremely tedious to do manually.

How to do it:

This import again only requires on line in the CSV, with the following values:

  1. Item Path: /sitecore/content/home/Articles
  2. Apply to Subitems: TRUE
  3. Template: Article Page
  4. When Placeholder Equals: Main Content Left Column
  5. Component Name: Page Metadata
  6. Placeholder: Header Content
  7. Before: Social Share Bar

example 2.PNG

The reason I used When Placeholder Equals is because I only wanted to move the Page Metadata when it was in the Main Content Left Column placeholder. If it was already in the header, or somewhere else on the page, I didn’t want to risk modifying it since it was either already in the right place, or somewhere else intentionally.

When run, this import moved the

Considerations:

The examples I provided were cases where only one instance of the component I needed to modify existed on each page. The import gets more difficult if, say, there are three Page Metadata components on the page; you will need to make sure to target the correct one, and use Nth of Type and/or When Placeholder Equals if you wish to modify other than the first one.

This consideration also applies to Before and After; if there were, say, three Social Share Bar components, Before would place the Page Metadata before the first Social Share Bar; After would place the Page Metadata after the last Social Share Bar.

One thing the import currently does not offer is the option to modify ALL matching components; you will have to add a new line with a different value for Nth of Type to update all instances of the component.

I recommend practicing the Rendering Parameters Import on some test items before making changes to real items. As always, use caution, especially when Publish Changes is checked, as this import can mess up your content if mistakes are made in the CSV.

You can download the new release on Github.

Sitecore components: Rendering Parameters or Datasource Templates?

In Sitecore, when you add a rendering to a page, there are several different ways to store settings/values for that rendering.

1. Rendering Parameters

The Rendering Parameters are specified on the Rendering item by the Parameters Template field.

2. Datasource Templates

The Datasource Template is specified on the Rendering item by the Datasource Template field.

Editing properties

Rendering Parameters can be edited either in the following ways:

In Content Editor:

  1. On your item, go to Presentation > Details

    presentation details

  2. Click on the rendering

    rendering parameters

  3. Edit the fields

In Experience Editor:

Select your rendering and click Edit Display Properties

EE.PNG

Datasource Templates can be edited in either of the following ways:

In Content Editor:

Browse to the Datasource Item in the Sitecore tree (usually in the item/Local Components folder by default, but may be elsewhere; you can find the datasource location by checking the Presentation Details)

datasource.PNG

In Experience Editor:

edit related item.PNG

You can also create custom buttons in the core database that will allow you to easily edit the datasource fields in a popup in Experience Editor for essentially the same experience as editing the Rendering Parameters. This requires some customization but is pretty easy to do.

Both the Rendering Parameters and the Datasource Item are equally easy to access in code when you’re building out your component. So why use one over the other?

Well, honestly I’m not sure why to ever use Rendering Parameters instead of the Datasource. The purpose of the Rendering Parameters is to pass additional information apart from the datasource (typically about how the rendering should display) to the component. Now while I understand the logic of the division between Rendering Parameters and Datasource Templates, I don’t see any good reason to put fields on the Rendering Parameters instead of the Datasource Template: it doesn’t actually seem to make anything easier apart from creating a division between data and display properties (since you can do all the same things with a datasource item that you can with rendering parameters, access it just as easily in code, and edit it the same way in EE with a custom button); the fields on the Rendering Parameters don’t get indexed whereas you can index anything on a Datasource template; and it makes modifying those fields significantly harder.

First of all, it can be much more difficult to find the right place to make changes to a component’s rendering parameters in Sitecore. A datasource appears as an object in the tree; you can browse to it, you can usually find the right item easily assuming it has a logical name and location, and you can find it with Sitecore’s search tools as you would with any other item. On the other hand, with rendering parameters, there is no item in the tree. You have to open the presentation details, find the right rendering — renderings all display by Rendering Name, so if there are multiple instances of the same rendering, they will all have the same name and it can be hard to figure out which one is the correct one to modify. Now it is easy to edit the rendering parameters in Experience Editor (since you have a visual display of all the renderings and can select the correct one more easily), but if you’re used to working mostly in Content Editor, it’s a more difficult process.

The big reason though why I would always choose Datasource Templates over Rendering Parameters is because it’s much harder to modify rendering parameters programatically than datasource items. I tend to do a lot of work writing scripts to programatically update items in bulk (such as the Content Export Tool), so for me, it’s valuable for fields to be easy to edit with a script. Fields in the Rendering Parameters cannot be exported or modified with the Content Export Tool whereas fields on datasource items can (though I would like to add that option in the future).

Datasource items can be modified as any other item, and doing so is easy (Note that the item in this case is the datasource item):

edit item.PNG

Programatically editing rendering parameters is much more difficult. Here is an example of some code that will modify the value of a field in the rendering parameters on an item (note that the item in this case is the page with the component on it):

edit params

Note: defaultDeviceId is the ID of /sitecore/layout/Devices/Default

You can see how the code for modifying the rendering parameters is much more complicated.

Now, I do see one benefit to the Rendering Parameters: you can easily determine the Placeholder the component is in:

foreach (RenderingDefinition rendering in deviceDefinition.Renderings)
{
var placeholderId = rendering.Placeholder;
…..

This is useful if you are trying to modify components only in particular placeholders. For example, my client had Dynamic Content Listing component that was used on hundreds of pages on the site. I added a new field to the Rendering Parameters (for consistency, since that’s where the other similar fields were) to Display Subtitle. This was fine, but the client needed hundreds of the existing components updated… but I couldn’t update ALL of the components by checking off the field in Standard Values, because the client only needed the new field to be checked on components that were in several specific placeholders, and explicitly needed it to NOT be checked on all other existing components.

So, I had to write a script that would automatically update this field in the Rendering Parameters for all instances of the component IF AND ONLY IF the component was within one of several specific placeholders.

edit parms placeholder.PNG

HOWEVER, everything I did here could STILL be more easily accomplished if the field was on the datasource template:

edit datasources.PNG

You can see from the example above that for my specific case of editing a field only when a component is in a specific placeholder, the code is about the same length and complexity for both Rendering Parameters and Datasource Templates. In other cases, when the placeholder doesn’t matter, it’s much easier to edit when the fields are on the Datasource Template. Therefore, I personally would always choose to use Datasource Templates over Rendering Parameters.

So you deleted Sitecore content, but it’s still online. Here’s a new module to help with that.

An issue I frequently see occur for my clients is that they’ve deleted an item from master, but it still exists in web. There are two ways to remove the item from web: publish the parent item with subitems; or switch to the web database and delete it directly in web.

In some cases the parent item cannot be published– namely, when the parent item is the site root. Publishing the site root with subitems will publish everything else in the site, and this is dangerous, as there may be content that is not supposed to go live yet. It is also not an option to publish the parent if the parent itself has changes that cannot be live yet.

Now, deleting in web is always a viable option, but may not be preferable for non-savvy users. It’s inherently risky to make changes directly in web, and furthermore, if they forget to switch back to master after making their deletion, that can cause some major problems if they proceed to create or modify content in web by mistake.

Now, every time this happens, I explain to my clients, “If you need to delete content and there is any chance you can’t publish the parent item, check off Never Publish and publish the item first, then delete from master.” This is the proper way to delete content when you can’t publish the parent item. But, people make mistakes, and my clients often forget to do this, and then they email me asking for help fixing it.

So now, let me introduce:

The Sitecore Deletion Workbox

The Deletion Workbox is a module that will track all items that have been deleted from master but still exist in at least one publishing target, and provide the option to either delete from all or selected publishing targets, or to restore from the recycle bin.

workbox
There are currently three items in the box; one publishing target (web) is selected, and one item is selected.

How does it work?

The module includes a custom handler that is patched into the deletion pipeline. When an item is deleted, if the item 1) was deleted from master and 2) is under the /sitecore/content path, its ID will be added to the Deleted Items field on /sitecore/system/Modules/Deletion Workbox/Workbox. This item is necessary for the workbox to work; if you delete it, the workbox will no longer work. Only items under /sitecore/content will be included (the reason for this being that the issue with not being able to publish the parent item really only occurs for content), and if your master database is not named “master”, it won’t work. I may change this later to be configurable, but for now, the module assumes that master is named master.

When you open the Deletion Workbox (which will appear on the right side of the Start menu), it will take all of those item IDs and check each target database for each ID. Assuming that item still exists in at least one publishing target, it will display that item and show its name, path, ID, and each target database that it exists in, as well as a Delete and Restore link. Clicking Delete under an item will immediately delete that single item from all selected target databases; Restore will restore it from the recycle bin.

item.PNG

There are four additional buttons at the bottom: Delete (selected); Delete (all); Restore (selected); and Restore (all).

buttons.PNG

Delete (all) and Restore (all) will delete or restore all items shown in the workbox. Delete (selected) and Restore (selected) will delete or restore all items that are checked off.

Finally, if you have more than one publishing target, make sure to pay attention to what publishing targets are selected. The publishing targets section is sticky, so that if you have a lot of items in the workbox and end up scrolling down, it will follow you to make sure you don’t forget to choose the correct target databases if necessary. If an item you are deleting does not exist in one of the selected targets, it will still work; the only concern is if you do NOT want to delete an item from a specific database.

targets.PNG

What about items that I deleted before I installed the module?

The module adds items to the Workbox during the deletion pipeline automatically, but if you want to pull in items that you deleted before you installed the module, you can use the Initialize button. This will scan every item in each target database and check to see if it exists in master, and will add it to the workbox if it does not. This can take a while, so use only as needed; it generally should not be needed more than once, since after the first initialization, all future deletions will be added automatically.

init

What about on-publish events?

You might be thinking, what about events that are supposed to be triggered during the publishing pipeline (i.e. reindexing to remove the deleted content from search). Will these events be triggered? Yes, they will!

In order to ensure that publish events still occur as they would if you were to publish the parent or check off Never Publish and publish the item from master, this is how the item deletion works:

  1. The item is found in the recycle bin, and restored to master (note: if an item does not exist in the recycle bin, it will not appear in the workbox).
  2. The item is modified so that __Never Publish is set to true.
  3. The item is published to all selected publishing targets.
  4. The item is deleted from master.

What about security? Can anyone delete items?

Don’t worry! Security settings are applied so that the workbox will only load items that the current user has permission to view and delete. There will be no way for a user to delete an item that they don’t have permission for.

 

This has been tested in Sitecore 8.2 and 9.0. You can download the module from github; it should be available soon on the Sitecore Marketplace as well.