PublishOptions failing when trying to publish media items programatically - c#

I'm trying to publish a media item programmatically but I am encountering a problem with the PublishOptions object. At runtime, the line of code where the PublishOptions object is being instantiated is breaking when I upload a media item. I get the following message saying:
"Value cannot be null. Parameter Name: item"
And the stacktrace is:
at Sitecore.Diagnostics.Assert.ArgumentNotNull(Object argument, String argumentName)
at Sitecore.Data.Managers.ItemProvider.ApplySecurity(Item item, SecurityCheck securityCheck)
at Sitecore.Data.Managers.ItemProvider.GetRootItem(Language language, Version version, Database database, SecurityCheck securityCheck)
at Sitecore.Data.Managers.ItemManager.GetRootItem(Language language, Version version, Database database)
at Sitecore.Nexus.Data.DataCommands.ResolvePathCommand.(String itemPath, Database database)
at Sitecore.Nexus.Data.DataCommands.ResolvePathCommand.(String itemPath, Database database)
at Sitecore.Nexus.Data.NexusDataApi.ResolvePath(String itemPath, Database database)
at Sitecore.Data.Engines.DataCommands.ResolvePathCommand.DoExecute()
at Sitecore.Data.Engines.EngineCommand`2.Execute()
at Sitecore.Data.Engines.DataEngine.ResolvePath(String itemPath)
at Sitecore.Data.Managers.ItemProvider.ResolvePath(String itemPath, Database database)
at Sitecore.Data.Managers.ItemProvider.GetItem(String itemPath, Language language, Version version, Database database, SecurityCheck
Here's the code where the PublishOptions object is being instantiated (where it is breaking):
public void OnItemSaved(Object sender, EventArgs args)
{
var item = Event.ExtractParameter(args, 0) as Item;
using (new SecurityDisabler())
{
if (item != null)
{
if (item.Paths.IsMediaItem)
{
var source = new Database("master");
var target = new Database("web");
var options = new PublishOptions(source, target, PublishMode.SingleItem, item.Language, DateTime.Now)
{
RootItem = item,
Deep = true,
};
var publisher = new Publisher(options);
publisher.PublishAsync();
}
}
}
}
What could be the reason why I'm getting this error?

Try this and see if this works, I am guessing it is to with the database.
public void OnItemSaved(Object sender, EventArgs args)
{
var item = Event.ExtractParameter(args, 0) as Item;
using (new SecurityDisabler())
{
if (item != null)
{
if (item.Paths.IsMediaItem)
{
var source = Factory.GetDatabase("master");
var target = Factory.GetDatabase("web");
var options = new PublishOptions(source, target,
PublishMode.SingleItem, item.Language,
DateTime.Now)
{
RootItem = item,
Deep = true,
};
var publisher = new Publisher(options);
publisher.PublishAsync();
}
}
}
}

I think you're being caught in a classic mistake. item:saved will also fire for your items as they're being published.
My initial guess would be, that you should expand your check to be
if ( item != null && item.Database.Name == "master" )
to prevent your code from attempting to publish the item, as the item:saved event fires on "web" during publishing.

Related

how to use Utils.FormIsOpen in c#

I need help using Utils in C#.
private void manageUsersToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!Utils.FormIsOpen("ManageUsers"))// this line gives an error
{
var manageUsers = new ManageUsers();
manageUsers.MdiParent = this;
manageUsers.Show();
}
}
I am using Microsoft SQL Server Management Studio 18 along with visual studio and I am trying to manage my users in the database while not physically adding them to the database but adding them at runtime.
It seems that there is no FormIsOpen method in Utils Class.
If you want to check if a "ManageUsers" has been opened, you can call Form.MdiChildren Property to get all mdichildforms and filter them.
// get all child form
Form[] children = this.MdiChildren;
var query = children.Select(c => c)
.Where(c => c is ManageUsers).ToList();
if(query.Count == 0)
{
var manageUsers = new ManageUsers();
manageUsers.MdiParent = this;
manageUsers.Show();
}

VSIX Get Project associated with its Context Menu

I want to create a context menu command for a project. I managed to get the command to show on the right menu by setting the id in the .vsct file to "IDM_VS_CTXT_PROJNODE". And my code example is called correctly:
private FirstCommand(AsyncPackage package, OleMenuCommandService commandService)
{
this.package = package ?? throw new ArgumentNullException(nameof(package));
commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
var menuCommandID = new CommandID(CommandSet, CommandId);
var menuItem = new MenuCommand(StartNotepad, menuCommandID);
commandService.AddCommand(menuItem);
}
private void StartNotepad(object sender, EventArgs e)
{
//example code
/*var process = new Process();
process.StartInfo.FileName = "Notepad.exe";
process.Start();*/
}
I now need Information about the project (directory, name etc). But all examples on vsix projects only show how to get the current project (I don't even know whether that is the project I clicked on) or don't work for me. They are all old and I don't know if they are currently best practice.
So my question is how do I get information about the project in StartNotepad()?
Thanks for your help.
Use the following method to get an item you clicked on:
private EnvDTE.UIHierarchyItem GetSelectedSolutionExplorerItem()
{
EnvDTE.UIHierarchy solutionExplorer = dte.ToolWindows.SolutionExplorer;
object[] items = solutionExplorer.SelectedItems as object[];
if (items.Length != 1)
return null;
return items[0] as EnvDTE.UIHierarchyItem;
}
And then convert it to a project with GetSelectedSolutionExplorerItem()?.Object as EnvDTE.Project.
Sergey's answer helped me to find the solution. The only think missing from it was how to get the dte in an async way:
private EnvDTE.UIHierarchyItem GetSelectedSolutionExplorerItem()
{
ThreadHelper.ThrowIfNotOnUIThread();
var dte = ServiceProvider.GetServiceAsync(typeof(DTE)).Result as DTE2;
if (dte == null) return null;
var solutionExplorer = dte.ToolWindows.SolutionExplorer;
object[] items = solutionExplorer.SelectedItems as object[];
if (items.Length != 1)
return null;
return items[0] as UIHierarchyItem;
}

How to prevent fill in values when saving over CSOM to a choice field

I am writing some ETL code to move data between an external system and SharePoint Online.
I am using the nuget package Microsoft.SharePointOnline.CSOM to communicate with SP in C#.
I am using the following code to update my field values.
spListItem[fieldName] = "Test Value";
spListItem.Update();
spClientContext.ExecuteQuery();
I noticed with Choice fields, if I save a non existing value SharePoint does not complain and just adds the value even if Allow 'Fill-in' choices is set to NO.
Is there a validate function anywhere in SharePoint? I saw some methods like ValidateUpdateListItem, but they didn't seem to do what I needed.
You could consider to validate choice field value before saving its value as demonstrated below:
static class ListItemExtensions
{
public static bool TryValidateAndUpdateChoiceFieldValue(this ListItem item, string fieldName, string fieldValue)
{
var ctx = item.Context;
var field = item.ParentList.Fields.GetByInternalNameOrTitle(fieldName);
ctx.Load(field);
ctx.ExecuteQuery();
var choiceField = ctx.CastTo<FieldChoice>(field);
if (!choiceField.FillInChoice)
{
var allowedValues = choiceField.Choices;
if (!allowedValues.Contains(fieldValue))
{
return false;
}
}
item.Update();
return true;
}
}
In that case the ListItem will be updated once the validation is
succeeded.
Usage
using (var ctx = new ClientContext(webUri))
{
var list = ctx.Web.Lists.GetByTitle(listTitle);
var listItem = list.GetItemById(itemId);
if(listItem.TryValidateAndUpdateChoiceFieldValue(fieldName,fieldValue))
ctx.ExecuteQuery();
}

Access TFS Team Query from Client Object API

Is there a way to run a shared team query, by name, through the TFS 2013 client object API
I'm working on a C# script that will do some work based off of the results of a shared team query. I don't want to have to maintain the query in the TFS UI as well as in my script; I'd prefer to just run the registered query that my team uses, but then just play with the results. When I write "registered query" I'm just referring to a query that I wrote in the TFS UI and saved as a shared query.
In other words: I'd like to use the TFS UI to create a query, save the file in my "shared queries" list, call it "foo", then access foo from the client object API in my script.
I see that there is a GetQueryDefinition(GUID) method off of WorkItemStore, but where would I get the GUID for a shared team query?
Sample code that should do what you need
///Handles nested query folders
private static Guid FindQuery(QueryFolder folder, string queryName)
{
foreach (var item in folder)
{
if (item.Name.Equals(queryName, StringComparison.InvariantCultureIgnoreCase))
{
return item.Id;
}
var itemFolder = item as QueryFolder;
if (itemFolder != null)
{
var result = FindQuery(itemFolder, queryName);
if (!result.Equals(Guid.Empty))
{
return result;
}
}
}
return Guid.Empty;
}
static void Main(string[] args)
{
var collectionUri = new Uri("http://TFS/tfs/DefaultCollection");
var server = new TfsTeamProjectCollection(collectionUri);
var workItemStore = server.GetService<WorkItemStore>();
var teamProject = workItemStore.Projects["TeamProjectName"];
var x = teamProject.QueryHierarchy;
var queryId = FindQuery(x, "QueryNameHere");
var queryDefinition = workItemStore.GetQueryDefinition(queryId);
var variables = new Dictionary<string, string>() {{"project", "TeamProjectName"}};
var result = workItemStore.Query(queryDefinition.QueryText,variables);
}

Creating sitecore item programmatically in specific language

I am trying to create a sitecore item programmatically. All working good. But the item I am creating from is in language "EN-GB" but new item created is in language "EN". Is there a way I can set the language value while creating an item.
var currentDatabase = Sitecore.Configuration.Factory.GetDatabase(selectedItem.Database.Name);
if (currentDatabase != null)
{
var rootItem = currentDatabase.GetItem(RootItemPath);
if (rootItem != null)
{
//Create new item
var item = rootItem.Add(selectedItem.Name, CommunityProjectTemplateId);
if (item != null)
{
item.Fields.ReadAll();
item.Editing.BeginEdit();
try
{
//Add values for the fields
item.Fields["Overview Body Content"].Value = selectedItem.GetStringField("ProjectOverview");
}
catch (Exception)
{
item.Editing.EndEdit();
}
}
}
}
You need to switch the context language before you retrieve the root item.
using (new LanguageSwitcher("en-gb"))
{
var rootItem = currentDatabase.GetItem(RootItemPath);
var item = rootItem.Add(selectedItem.Name, CommunityProjectTemplateId);
// Do your editing on item here...
}
If you are trying to add an item in en-GB then make sure that the "Root Item" that you get is in en-GB by doing this.
Language language;
Language.TryParse("en-GB", out language);
var rootItem = currentDatabase.GetItem(RootItemID, language);
After that if you try to add an item it will be in that language.
Using the Language Switcher is a great idea as well but be careful when changing language context.
You can use the following code before creating an item
var language = Sitecore.Globalization.Language.Parse( "en-GB" );
Sitecore.Context.SetLanguage( language, true );
The last line will set the context language to en-GB. Then the item will be created with that language version.

Categories