在Sitecore内容管理中,经常会遇到需要批量创建多个相同模板项的情况。默认的Sitecore只支持一次创建一个项,这对于需要创建多个类似项(如卡片、图片等)的场景来说效率较低。本文将介绍如何扩展Sitecore的插入功能,实现一次性创建多个项的功能。
首先创建继承自Sitecore.Shell.Web.UI.WebControls.RibbonPanel的命令类, 参考Sitecore.Shell.Applications.ContentManager.Panels.NewPanel
public class CustomNewPanel : RibbonPanel
{
/// <summary>Renders the panel.</summary>
/// <param name="output">The output.</param>
/// <param name="ribbon">The ribbon.</param>
/// <param name="button">The button.</param>
/// <param name="context">The context.</param>
public override void Render(
HtmlTextWriter output,
Ribbon ribbon,
Item button,
CommandContext context)
{
Sitecore.Diagnostics.Assert.ArgumentNotNull((object)output, nameof(output));
Sitecore.Diagnostics.Assert.ArgumentNotNull((object)ribbon, nameof(ribbon));
Sitecore.Diagnostics.Assert.ArgumentNotNull((object)button, nameof(button));
Sitecore.Diagnostics.Assert.ArgumentNotNull((object)context, nameof(context));
Item[] items = context.Items;
if (items.Length != 1)
return;
Item obj1 = items[0];
if (!obj1.Access.CanCreate() || !obj1.Access.CanWriteLanguage())
return;
List<Item> masters = Sitecore.Data.Masters.Masters.GetMasters(obj1);
List<CustomNewPanel.InsertOption> options = new List<CustomNewPanel.InsertOption>();
foreach (Item master in masters)
options.Add(new CustomNewPanel.InsertOption(master.GetUIDisplayName(), master.Appearance.Icon, $"item:createmultiple(master={master.ID})"));
//foreach (Item obj2 in ItemUtil.GetChildrenAt("/sitecore/content/Applications/Content Editor/Menues/New"))
// options.Add(new CustomNewPanel.InsertOption(obj2["display name"], obj2["icon"], obj2["message"].Replace("$Target", obj1.ID.ToString())));
output.Write("<div id=\"CustomNewPanelHolder\" class=\"scRibbonGallery\">");
output.Write("<div role=\"menu\" aria-orientation=\"vertical\" id=\"CustomNewPanelList\" class=\"scRibbonGalleryList\" onkeydown=\"scForm.onMenuKeyDown(this, event, false)\">");
this.RenderOptions(output, options);
output.Write("</div>");
this.RenderPanelButtons(output, "CustomNewPanelList", this.GetClick(obj1));
output.Write("</div>");
}
protected new void RenderPanelButtons(HtmlTextWriter output, string id, string click)
{
Assert.ArgumentNotNull(output, "output");
Assert.ArgumentNotNull(id, "id");
Assert.ArgumentNotNull(click, "click");
output.Write("<div class=\"scRibbonPanelButtons\">");
ImageBuilder imageBuilder = new ImageBuilder();
imageBuilder.Src = "Images/Ribbon/ribbon_panel_up.png";
imageBuilder.Class = "scRibbonPanelUp";
imageBuilder.RollOver = true;
imageBuilder.Disabled = !Enabled;
imageBuilder.OnClick = "javascript:scContent.scrollPanel('" + id + "', true)";
output.Write(imageBuilder.ToString());
imageBuilder = new ImageBuilder();
imageBuilder.Src = "Images/Ribbon/ribbon_panel_down.png";
imageBuilder.Class = "scRibbonPanelDown";
imageBuilder.RollOver = true;
imageBuilder.Disabled = !Enabled;
imageBuilder.OnClick = "javascript:scContent.scrollPanel('" + id + "', false)";
output.Write(imageBuilder.ToString());
output.Write("</div>");
}
/// <summary>Gets the click.</summary>
/// <param name="item">The item.</param>
/// <returns>The click.</returns>
protected virtual string GetClick(Item item)
{
Sitecore.Diagnostics.Assert.ArgumentNotNull((object)item, nameof(item));
UrlString url = CustomNewPanel.GetUrl(item);
string empty = string.Empty;
string height = "250";
GalleryManager.GetGallerySize("NewGallery", ref empty, ref height);
return "javascript:return scContent.showGallery(scForm.browser.getControl('CustomNewPanelHolder'), event, 'NewGallery','Gallery.New','" + (object)url + "','" + empty + "','" + height + "','expanding')";
}
/// <summary>Gets the URL.</summary>
/// <param name="item">The item.</param>
/// <returns>The URL.</returns>
protected static UrlString GetUrl(Item item)
{
Sitecore.Diagnostics.Assert.ArgumentNotNull((object)item, nameof(item));
UrlString url = new UrlString();
url.Append("id", item.ID.ToString());
url.Append("la", item.Language.ToString());
url.Append("vs", item.Version.ToString());
url.Append("db", item.Database.Name);
return url;
}
/// <summary>Renders the masters.</summary>
/// <param name="output">The output.</param>
/// <param name="options">The masters.</param>
private void RenderOptions(HtmlTextWriter output, List<CustomNewPanel.InsertOption> options)
{
Sitecore.Diagnostics.Assert.ArgumentNotNull((object)output, nameof(output));
Sitecore.Diagnostics.Assert.ArgumentNotNull((object)options, "masters");
if (options.Count <= 0)
{
output.Write("<div style=\"padding:4px; color:#333333;white-space:normal;width:200px\">" + Translate.Text("No Insert Options Available") + "</div>");
}
else
{
for (int index = 0; index < options.Count; ++index)
{
CustomNewPanel.InsertOption option = options[index];
string clientEvent = Context.ClientPage.GetClientEvent(option.Click);
string str = string.Format("({0} {1} {2})", (object)(index + 1), (object)Translate.Text("of"), (object)options.Count);
if (this.Enabled)
output.Write("<a role=\"menuitem\" aria-label=\"" + option.DisplayName + " " + str + "\" href=\"#\" class=\"scRibbonToolbarSmallButton\" title=\"" + StringUtil.EscapeQuote(option.DisplayName) + "\" onclick=\"" + clientEvent + "\"" + (index > 0 ? " tabindex=\"-1\"" : "") + ">");
else
output.Write("<span class=\"scRibbonToolbarSmallButtonDisabled\">");
output.Write("<span class=\"scRibbonToolbarSmallButtonPrefix header\">");
output.Write(str);
output.Write("</span>");
output.Write("<span class=\"scRibbonToolbarSmallButtonLabel header \">{0}{1}</span>", (object)new ImageBuilder()
{
Src = Images.GetThemedImageSource(option.Icon, ImageDimension.id16x16),
Class = "scRibbonToolbarSmallButtonIcon",
Disabled = !this.Enabled,
Alt = option.DisplayName
}, (object)StringUtil.Clip(option.DisplayName, 50, true));
if (this.Enabled)
output.Write("</a>");
else
output.Write("</span>");
}
}
}
/// <summary>Insert option.</summary>
[DebuggerDisplay("{DisplayName} {Click}")]
private class InsertOption
{
/// <summary>
/// Initializes a new instance of the <see cref="T:Sitecore.Shell.Applications.ContentManager.Panels.NewPanel.InsertOption" /> class.
/// </summary>
/// <param name="displayName">The display name.</param>
/// <param name="icon">The icon.</param>
/// <param name="click">The click.</param>
public InsertOption(string displayName, string icon, string click)
{
this.Click = click;
this.DisplayName = displayName;
this.Icon = icon;
}
/// <summary>Gets the Click event.</summary>
/// <value>The event to be executed.</value>
public string Click { get; }
/// <summary>Gets the display name.</summary>
/// <value>The display name.</value>
public string DisplayName { get; }
/// <summary>Gets the icon.</summary>
/// <value>The icon.</value>
public string Icon { get; }
}
}
创建继承自Sitecore.Shell.Framework.Commands.Command的命令类
public class CreateMultipleItems : Command
{
/// <summary>Executes the command in the specified context.</summary>
/// <param name="context">The context.</param>
public override void Execute(CommandContext context)
{
if (context.Items.Length != 1 || !context.Items[0].Access.CanCreate())
return;
Item obj = context.Items[0];
Context.ClientPage.Start((object)this, "Add", new NameValueCollection()
{
["Master"] = context.Parameters["master"],
["ItemID"] = obj.ID.ToString(),
["Language"] = obj.Language.ToString(),
["Version"] = obj.Version.ToString()
});
}
/// <summary>Queries the state of the command.</summary>
/// <param name="context">The context.</param>
/// <returns>The state of the command.</returns>
public override CommandState QueryState(CommandContext context)
{
Error.AssertObject((object)context, nameof(context));
if (context.Items.Length != 1)
return CommandState.Hidden;
return !context.Items[0].Access.CanCreate() ? CommandState.Disabled : base.QueryState(context);
}
/// <summary>Adds the specified args.</summary>
/// <param name="args">The arguments.</param>
protected void Add(ClientPipelineArgs args)
{
if (!SheerResponse.CheckModified())
return;
Item master = Context.ContentDatabase.GetItem(args.Parameters["Master"]);
if (master == null)
SheerResponse.Alert("Branch \"{0}\" not found.", args.Parameters["Master"]);
if (args.IsPostBack)
{
if (!args.HasResult)
return;
// 解析返回的结果
var result = StringUtil.ParseNameValueCollection(args.Result, '&', '=');
var baseName = result["basename"];
var quantity = int.Parse(result["quantity"]);
Item parent = Context.ContentDatabase.Items[StringUtil.GetString(args.Parameters["ItemID"]), Language.Parse(StringUtil.GetString(args.Parameters["Language"]))];
if (parent == null)
SheerResponse.Alert("Parent item not found.");
else if (!parent.Access.CanCreate())
Context.ClientPage.ClientResponse.Alert("You do not have permission to create items here.");
else if (!Sitecore.Data.Masters.Masters.GetMasters(parent).Any<Item>((Func<Item, bool>)(x => x.ID == master.ID)))
{
Context.ClientPage.ClientResponse.Alert("You do not have permission to create an item here.");
}
else
{
// 创建项目
var creationResult = CreateItemsWithNameCheck(parent, master, baseName, quantity);
// 显示创建结果
if (creationResult.SkippedItems > 0)
{
Log.Info(
$"Created {creationResult.CreatedItems} items.\n" +
$"Skipped {creationResult.SkippedItems} items due to existing names.", this);
SheerResponse.Alert(
$"Created {creationResult.CreatedItems} items.\n" +
$"Skipped {creationResult.SkippedItems} items due to existing names.");
}
}
}
else
{
SheerResponse.ShowModalDialog(
Sitecore.UIUtil.GetUri("control:CreateMultipleItems"),
"400", "300", string.Empty, true);
args.WaitForPostBack();
}
}
protected class CreationResult
{
public int CreatedItems { get; set; }
public int SkippedItems { get; set; }
public int StartIndex { get; set; }
public int EndIndex { get; set; }
}
protected CreationResult CreateItemsWithNameCheck(Item parent, Item master, string baseName, int quantity)
{
var result = new CreationResult();
// 获取已存在的子项名称
var existingNames = new HashSet<string>(
parent.Children
.Select(x => x.Name)
.Where(n => n.StartsWith(baseName, StringComparison.OrdinalIgnoreCase))
);
// 找出已使用的最大序号
var maxNumber = existingNames
.Select(name =>
{
int num;
var match = Regex.Match(name, @"\d+$");
return match.Success && int.TryParse(match.Value, out num) ? num : 0;
})
.DefaultIfEmpty(0)
.Max();
// 从最大序号后开始创建
int startIndex = maxNumber + 1;
using (new SecurityDisabler())
{
for (int i = 0; i < quantity; i++)
{
var currentIndex = startIndex + i;
var name = $"{baseName}{currentIndex}";
// 检查名称是否已存在
if (existingNames.Contains(name))
{
result.SkippedItems++;
continue;
}
try
{
Item obj = (Item)null;
if (master.TemplateID == TemplateIDs.BranchTemplate)
{
BranchItem branch = (BranchItem)master;
obj = Context.Workflow.AddItem(name, branch, parent);
Log.Audit(this, "Add item name: [{0}] from branch: {1}", name, AuditFormatter.FormatItem((Item)branch));
}
else
{
TemplateItem template = (TemplateItem)master;
obj = Context.Workflow.AddItem(name, template, parent);
Log.Audit(this, "Add item name: [{0}] from template: {1}", name, AuditFormatter.FormatItem((Item)template));
}
if(obj != null)
result.CreatedItems++;
}
catch (Exception ex)
{
Log.Error($"Failed to create item '{name}'", ex, this);
result.SkippedItems++;
}
}
}
result.StartIndex = startIndex;
result.EndIndex = startIndex + quantity - 1;
return result;
}
}
在/sitecore/shell/Applications/Dialogs/CreateMultiItems/下创建CreateMultipleItems.xml:
<?xml version="1.0" encoding="utf-8" ?>
<control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense">
<CreateMultipleItems>
<CodeBeside Type="Sitecore.Foundation.SitecoreExtensions.sitecore.shell.Applications.Dialogs.CreateMultiItems.CreateMultipleItemsForm, Sitecore.Foundation.SitecoreExtensions"/>
<FormDialog Icon="Applications/32x32/document_new.png" Header="Create Multiple Items" Text="Create multiple items from selected template.">
<Border Class="scFlexColumnContainer">
<Border Class="scFormRow">
<Border Class="scFormLabel">Base name:</Border>
<Edit ID="BaseName" Class="scContentControl"/>
</Border>
<Border Class="scFormRow">
<Border Class="scFormLabel">Number of items:</Border>
<Combobox ID="Quantity" Class="scContentControl"/>
</Border>
</Border>
</FormDialog>
</CreateMultipleItems>
</control>
对话框代码后置:
public class CreateMultipleItemsForm : DialogForm
{
protected Edit BaseName;
protected Combobox Quantity;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!Context.ClientPage.IsEvent)
{
// 添加数量选项
Quantity.Controls.Clear();
for (int i = 1; i <= 10; i++)
{
Quantity.Controls.Add(new ListItem() { Name = i.ToString(), Value = i.ToString(), Selected = i == 1 });
}
}
}
protected override void OnOK(object sender, EventArgs args)
{
// 验证输入
if (string.IsNullOrEmpty(BaseName.Value))
{
SheerResponse.Alert("Please enter a base name.");
return;
}
// 获取选择的数量
var quantity = Quantity.SelectedItem.Value ?? "1";
// 返回结果
var result = $"basename={BaseName.Value}&quantity={quantity}";
SheerResponse.SetDialogValue(result);
base.OnOK(sender, args);
}
}
在配置文件中注册命令:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<commands>
<command name="item:createmultiple" type="Sitecore.Foundation.SitecoreExtensions.Commands.CreateMultipleItems, Sitecore.Foundation.SitecoreExtensions"/>
</commands>
</sitecore>
</configuration>
通过扩展Sitecore的命令系统,我们实现了一个实用的批量创建功能。该实现不仅提高了内容编辑效率,也展示了如何正确集成到Sitecore框架中。关键是遵循Sitecore的最佳实践,确保功能的可靠性和可维护性。
还没有人评论,抢个沙发吧...