Sitecore workflow approval state query - sitecore

I have created workflow in my sitecore project and on final state ( Approval ) I just want auto publish to a particular database.
So where should I do the changes to point to database.
Thanks

In order to perform automatic publishing, your final state should contain a workflow action, that does the job for you. You may take a look on Sample Workflow (that comes by default with Sitecore) - Approved state. It contains child item Auto Publish, that has two fields.
Type string:
Sitecore.Workflows.Simple.PublishAction, Sitecore.Kernel
sets the class that in fact does publishing. You may inherit from that class and implement your own behavior, supply extra parameters etc. I would advise you to take dotPeek or Reflector and look-up this class implementation so that you may adjust your own code.
Parameters:
deep=0
..stands for publishing child items recursively.
Update: Lets take a look on decompiled class from Sample Workflow Auto Publish action:
public class PublishAction
{
public void Process(WorkflowPipelineArgs args)
{
Item dataItem = args.DataItem;
Item innerItem = args.ProcessorItem.InnerItem;
Database[] targets = this.GetTargets(dataItem);
PublishManager.PublishItem(dataItem, targets, new Language[1]
{
dataItem.Language
}, (this.GetDeep(innerItem) ? 1 : 0) != 0, 0 != 0);
}
private bool GetDeep(Item actionItem)
{
return actionItem["deep"] == "1" || WebUtil.ParseUrlParameters(actionItem["parameters"])["deep"] == "1";
}
private Database[] GetTargets(Item item)
{
using (new SecurityDisabler())
{
Item obj = item.Database.Items["/sitecore/system/publishing targets"];
if (obj != null)
{
ArrayList arrayList = new ArrayList();
foreach (BaseItem baseItem in obj.Children)
{
string name = baseItem["Target database"];
if (name.Length > 0)
{
Database database = Factory.GetDatabase(name, false);
if (database != null)
arrayList.Add((object)database);
else
Log.Warn("Unknown database in PublishAction: " + name, (object)this);
}
}
return arrayList.ToArray(typeof(Database)) as Database[];
}
}
return new Database[0];
}
}
GetTargets() method from above default example does publishing to all targets that are specified under /sitecore/system/publishing targets path. As I mentioned above, you may create your own class with your own implementation and reference that from workflow action definition item.

You can look into Sample workflow's Auto publish action. But in general you can create a Workflow Action with type: Sitecore.Workflows.Simple.PublishAction, Sitecore.Kernel and set parameters as deep=1&related=1&targets=somedb,web&alllanguages=1

Related

Acumatica - Action "Reverse and Apply Memo" Default Post Period to active financial period

Can a customization be created to set the post period to the current active financial period after pressing the "Reverse and Apply to Memo" action in "Invoices and Memos" screen?
We've noticed the newly created credit memo defaults to the post period of the invoice which could be incorrect if it's credited in the following financial period.
The solution defined below was developed within Acumatica 20.102.0015 and changes the date and post period for the created credit memo on "Reverse and Apply Memo" action to the default of a new document instead of the date from the reversed invoice.
namespace AARAMPostPeriod
{
public class AAARInvoiceEntryExtension : PXGraphExtension<ARInvoiceEntry>
{
public delegate IEnumerable ReverseDocumentAndApplyToReversalIfNeededDel(PXAdapter adapter, ReverseInvoiceArgs reverseArgs);
[PXOverride]
public virtual IEnumerable ReverseDocumentAndApplyToReversalIfNeeded(PXAdapter adapter, ReverseInvoiceArgs reverseArgs, ReverseDocumentAndApplyToReversalIfNeededDel del)
{
if(reverseArgs.ApplyToOriginalDocument) reverseArgs.DateOption = ReverseInvoiceArgs.CopyOption.SetDefault;
return del(adapter, reverseArgs);
}
}
}
Default value for reverseArgs.DateOption is typically
ReverseInvoiceArgs.CopyOption.SetOriginal
You are talking about the receivables side of things, but I did something similar on the payables side. It isn't exactly what you are asking for, but it is too big for a comment.
You may be able to get the general idea from this and apply to your scenario. The approach I took was to check the period when releasing.
protected virtual void _(Events.FieldUpdated<APInvoice.finPeriodID> e)
{
APInvoice row = (APInvoice)e.Row;
CheckPeriod(e.Cache, row);
}
#region Release override
public delegate IEnumerable ReleaseDelegate(PXAdapter adapter);
[PXOverride]
public virtual IEnumerable Release(PXAdapter adapter, ReleaseDelegate baseMethod)
{
CheckPeriod(Base.Caches[typeof(APInvoice)], Base.Document.Current);
return baseMethod(adapter);
}
#endregion
protected virtual void CheckPeriod(PXCache cache, APInvoice invoice)
{
if (invoice?.FinPeriodID == null) return;
string currentPeriod = GetCurrentPeriod(invoice.BranchID);
if (currentPeriod != invoice.FinPeriodID)
{
PXUIFieldAttribute.SetError<APInvoice.finPeriodID>(cache, invoice, "Invalid period");
}
}
public virtual string GetCurrentPeriod(int? branchID)
{
PXResultset<Branch> Results = PXSelectJoin<GL.Branch,
InnerJoin<FinPeriod, On<FinPeriod.organizationID, Equal<Branch.organizationID>>>,
Where<Branch.branchID, Equal<Required<Branch.branchID>>,
And<FinPeriod.startDate, LessEqual<Required<FinPeriod.startDate>>,
And<FinPeriod.endDate, Greater<Required<FinPeriod.endDate>>>>>> // End Date is the date AFTER the period ends
.SelectSingleBound(Base, null, branchID, Base.Accessinfo.BusinessDate, Base.Accessinfo.BusinessDate);
if (Results != null)
{
foreach (PXResult<GL.Branch, FinPeriod> result in Results)
{
FinPeriod period = result;
return period.FinPeriodID;
}
}
return null;
}
As you can see, I put an override on the Release to perform my validation which sets an error condition if the period is not current. The validation is performed by retrieving the current period of the current business date and comparing to the period on the APInvoice.
You could explore leveraging GetCurrentPeriod from the example and put into an override on FieldDefaulting if it helps with your goal.

Glass Mapper V4 Language Item Fallback, how to make it work with Autofac?

I am using Glass Mapper v4 with Autofac and cant figure out how to make it work with the Language Item Fall back module. There are examples of creating a class that implements IObjectConstructionTask (see below)
public class FallbackCheckTask : IObjectConstructionTask
{
public void Execute(ObjectConstructionArgs args)
{
if (args.Result == null)
{
var scContext = args.AbstractTypeCreationContext as SitecoreTypeCreationContext;
// if the item itself is null, regardless of version, abort
if (scContext.Item == null)
{
args.AbortPipeline();
return;
}
// we could be trying to convert rendering parameters to a glass model, and if so, just return.
if (String.Compare(scContext.Item.Paths.FullPath, "[orphan]/renderingParameters", true) == 0)
{
return;
}
// the default glassmapper code would simply abort pipeline if the context items version count for the current langauge was 0
// but this does not take item fallback into account
// added here a check on the fallback extension method GetFallbackItem, recursively (for chained fallback)
// and then if that fallback item is null or it's version count is 0 (and only then) would you go ahead and abort the pipeline
if (scContext.Item.Versions.Count == 0)
{
var fallBackItem = CheckRecursivelyForFallbackItem(scContext.Item);
if (fallBackItem == null)
args.AbortPipeline();
else if (fallBackItem.Versions.Count == 0)
args.AbortPipeline();
return;
}
}
}
// in the case of chained fallback, eg fr-CA -> en-CA -> en
// could be that the middle languages don't have versions either, but DO have a fallback item
// therefore, must check back further until either a version is found, or there are no more fallback items
private Item CheckRecursivelyForFallbackItem(Item thisItem)
{
var fallBackItem = thisItem.GetFallbackItem();
if (fallBackItem != null)
{
if (fallBackItem.Versions.Count == 0)
fallBackItem = CheckRecursivelyForFallbackItem(fallBackItem);
}
return fallBackItem;
}
}
Then you register (with Castle Windsor)
public static void CastleConfig(IWindsorContainer container){
var config = new Config();
container.Register(
Component.For<IObjectConstructionTask>().ImplementedBy<FallbackCheckTask>().LifestylePerWebRequest()
);
// config.EnableCaching = false;
container.Install(new SitecoreInstaller(config));
}
I am using Autofac and do not know how to perform the same action as above and assure it happens in the right order. I am registering my types the typical way (See below) but it doesn't seem to be hooking my FallbackCheckTask class.
public static void Register()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
// register our types
builder.RegisterType<FallbackCheckTask>().As<IObjectConstructionTask>().InstancePerLifetimeScope();
// build and set the resolver
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
I also have the Language Item Fallback wired up and working as expected if glass is not involved in fetching the items values. I understand why Glass is not mapping the data out of the box, I just cant seem to get the fix working. Any thoughts?
EDIT 2015-05-21 19:00
I edited GlassMapperScCustom.cs as follows:
public static IDependencyResolver CreateResolver(){
var config = new Glass.Mapper.Sc.Config();
var resolver = new DependencyResolver(config);
resolver.ObjectConstructionFactory.Add(() => new FallbackCheckTask());
return resolver;
}
And now its calling the Execute method of the the FallbackCheckTask only if there is a version of the item, if there is no version its not calling the method. Also, no matter what I do if I enable this Task my test query items always come back as NULL:
var test = SitecoreContext.QuerySingle<Item>("{7A6D933A-127B-4C08-B073-7C39F16EBD06}");
var test1 = SitecoreContext.Query<Item>("{7A6D933A-127B-4C08-B073-7C39F16EBD06}").ToList();
var test2 = SitecoreContext.GetCurrentItem<Item>();
var test3 = SitecoreContext.GetItem<Item>("{7A6D933A-127B-4C08-B073-7C39F16EBD06}");
So to sum it up, I am a little better off now then I was before but when the task class is registered queries come back as null for all items no matter if they have a version or not. As mentioned before, any help is appreciated.
I know you mentioned that you were using the VersionCountDisabler but does it look like this:
using(new VersionCountDisabler()){
var test = SitecoreContext.QuerySingle<Item>("{7A6D933A-127B-4C08-B073-7C39F16EBD06}");
var test1 = SitecoreContext.Query<Item>("{7A6D933A-127B-4C08-B073-7C39F16EBD06}").ToList();
var test2 = SitecoreContext.GetCurrentItem<Item>();
var test3 = SitecoreContext.GetItem<Item>("{7A6D933A-127B-4C08-B073-7C39F16EBD06}");
}
Or are you disabling it in some other manner?
I also notice that your fallback code doesn't seem to update the scContent.Item property. I think you need to update it to the following:
public class FallbackCheckTask : IObjectConstructionTask
{
public void Execute(ObjectConstructionArgs args)
{
if (args.Result == null)
{
var scContext = args.AbstractTypeCreationContext as SitecoreTypeCreationContext;
// if the item itself is null, regardless of version, abort
if (scContext.Item == null)
{
args.AbortPipeline();
return;
}
// we could be trying to convert rendering parameters to a glass model, and if so, just return.
if (String.Compare(scContext.Item.Paths.FullPath, "[orphan]/renderingParameters", true) == 0)
{
return;
}
// the default glassmapper code would simply abort pipeline if the context items version count for the current langauge was 0
// but this does not take item fallback into account
// added here a check on the fallback extension method GetFallbackItem, recursively (for chained fallback)
// and then if that fallback item is null or it's version count is 0 (and only then) would you go ahead and abort the pipeline
if (scContext.Item.Versions.Count == 0)
{
var fallBackItem = CheckRecursivelyForFallbackItem(scContext.Item);
if (fallBackItem == null)
args.AbortPipeline();
else if (fallBackItem.Versions.Count == 0)
args.AbortPipeline();
//don't just return but update the scContext.Item to the fallback item
scContext.Item = fallbackItem;
}
}
}
// in the case of chained fallback, eg fr-CA -> en-CA -> en
// could be that the middle languages don't have versions either, but DO have a fallback item
// therefore, must check back further until either a version is found, or there are no more fallback items
private Item CheckRecursivelyForFallbackItem(Item thisItem)
{
var fallBackItem = thisItem.GetFallbackItem();
if (fallBackItem != null)
{
if (fallBackItem.Versions.Count == 0)
fallBackItem = CheckRecursivelyForFallbackItem(fallBackItem);
}
return fallBackItem;
}
}

Writing a test class on a Trigger in Salesforce

I am a complete code noob and need help writing a test class for a trigger in Salesforce. Any help would be greatly appreciated.
Here is the trigger:
trigger UpdateWonAccounts on Opportunity(before Update) {
Set < Id > accountIds = new Set < Id > ();
//Collect End user Ids which has won Opportunities
for (Opportunity o : Trigger.new) {
if (o.isWon && o.EndUserAccountName__c != null) {
accountIds.add(o.EndUserAccountName__c);
}
}
List < Account > lstAccount = new List < Account > ();
//Iterate and collect all the end user records
for (Account a : [Select Id, Status__c From Account where Id IN : accountIds]) {
lstAccount.add(new Account(Id = a.Id, Status__c = true));
}
//If there are any accounts then update the records
if (!lstAccount.isEmpty()) {
update lstAccount;
}
}
Read An Introduction to Apex Code Test Methods and How To Write A Trigger Test.
Basically, you want to create a new testmethod that updates (inserts, deletes, undeletes, etc. depending on your trigger conditions) a record or sObject.
It looks somewhat like this:
public class myClass {
static testMethod void myTest() {
// Add test method logic to insert and update a new Opportunity here
}
}

Sitecore ASP.NET - manage aliases per domain

I have site running Sitecore 6.3. This site include some subsites (for example www.a.com & www.b.com). I want to ask: is it possible to create separate aliases for this sites (e.g. www.a.com/alias & www.b.com/alias should redirect to different pages). Now if create an alias for site www.a.com it will be also in www.b.com. Any ideas how to manage this?
Thnx.
This is possible. I have already made a Sitecore support ticket for this. I have also worked this out and for me their solution worked fine. (haven't implemented it on the live website yet, because we had no agreement on the costs yet). This is some sample code you might want to look at:
class MultiSiteAliasResolver : AliasResolver
{
public new void Process(HttpRequestArgs args)
{
Assert.ArgumentNotNull(args, "args");
if (!Settings.AliasesActive)
{
Tracer.Warning("Aliases are not active.");
}
else
{
Sitecore.Data.Database database = Sitecore.Context.Database;
if (database == null)
{
Tracer.Warning("There is no context database in AliasResover.");
}
Item aliasItem = getAliasItem(args);
if (aliasItem != null)
{
LinkField linkField = aliasItem.Fields["Linked item"];
if (linkField != null)
{
Item AliasLinkedTo = Sitecore.Context.Database.GetItem(linkField.TargetID);
if (AliasLinkedTo != null)
{
Sitecore.Context.Item = AliasLinkedTo;
}
}
else
{
base.Process(args);
}
}
}
}
/// <summary>
/// Gets the alias item.
/// </summary>
/// <param name="args">The args.</param>
/// <returns></returns>
private Item getAliasItem(HttpRequestArgs args)
{
string websitePath = Sitecore.Context.Site.RootPath.ToLower();
if (args.LocalPath.Length > 1)
{
Item aliasItem = Sitecore.Context.Database.GetItem(websitePath + "/settings/aliassen/" + args.LocalPath);
if (aliasItem != null)
{
return aliasItem;
}
}
return null;
}
}
This class could be inserted in the web.config in place of the AliasResolver. For example:
<processor type="CommandTemplates.Classes.MultiSiteAliasResolver, CommandTemplates" />
I hope this will work for you, good luck!
update: In my example I have a folder under each website node "/settings/aliassen/", that's the location I want the users to set alliases. By the way, also notice that when you change the AliasResolver like this the window that the standard Sitecore Alias button triggers won't have the needed functionality anymore. I haven't had any time to dins a way to make that work, however you could always explain the content managers how to work with your new solution.

Scopes not created from a template cannot have FilterParameters Error

I am trying to build on the "WebSharingAppDemo-SqlProviderEndToEnd" msdn sample application to build out a custom MSF implementation. As part of that I added parameterized filters to the provisioning. I have been referencing http://jtabadero.wordpress.com/2010/09/02/sync-framework-provisioning/ for some idea of how to do this. Now that I have that in place, when I re-initialize the "peer1" database and try to provision it initially I now get an error:
Scopes not created from a template cannot have FilterParameters.
Parameter '#my_param_name' was found on Table '[my_table_name]'.
Please ensure that no FilterParameters are being defined on a scope
that is not created from a template.
The only guess I have as to what a "template" is, is the provisioning templates that the Sync Toolkit's tools can work with, but I don't think that applies in the scenario I'm working with.
I have been unable to find anything that would indicate what I should do to fix this. So how can I get past this error but still provision my database with parameterized filters?
The below code is what I'm using to build the filtering into the provisioning (SqlSyncScopeProvisioning) object.
private void AddFiltersToProvisioning(IEnumerable<TableInfo> tables)
{
IEnumerable<FilterColumn> filters = this.GetFilterColumnInfo();
foreach (TableInfo tblInfo in tables)
{
this.AddFiltersForTable(tblInfo, filters);
}
}
private void AddFiltersForTable(TableInfo tblInfo, IEnumerable<FilterColumn> filters)
{
IEnumerable<FilterColumn> tblFilters;
tblFilters = filters.Where(x => x.FilterLevelID == tblInfo.FilterLevelID);
if (tblFilters != null && tblFilters.Count() > 0)
{
var tblDef = this.GetTableColumns(tblInfo.TableName);
StringBuilder filterClause = new StringBuilder();
foreach (FilterColumn column in tblFilters)
{
this.AddColumnFilter(tblDef, column.ColumnName, filterClause);
}
this.Provisioning.Tables[tblInfo.TableName].FilterClause = filterClause.ToString();
}
}
private void AddColumnFilter(IEnumerable<TableColumnInfo> tblDef, string columnName, StringBuilder filterClause)
{
TableColumnInfo columnInfo;
columnInfo = tblDef.FirstOrDefault(x => x.ColumnName.Equals(columnName, StringComparison.CurrentCultureIgnoreCase));
if (columnInfo != null)
{
this.FlagColumnForFiltering(columnInfo.TableName, columnInfo.ColumnName);
this.BuildFilterClause(filterClause, columnInfo.ColumnName);
this.AddParamter(columnInfo);
}
}
private void FlagColumnForFiltering(string tableName, string columnName)
{
this.Provisioning.Tables[tableName].AddFilterColumn(columnName);
}
private void BuildFilterClause(StringBuilder filterClause, string columnName)
{
if (filterClause.Length > 0)
{
filterClause.Append(" AND ");
}
filterClause.AppendFormat("[base].[{0}] = #{0}", columnName);
}
private void AddParamter(TableColumnInfo columnInfo)
{
SqlParameter parameter = new SqlParameter("#" + columnInfo.ColumnName, columnInfo.GetSqlDataType());
if (columnInfo.DataTypeLength > 0)
{
parameter.Size = columnInfo.DataTypeLength;
}
this.Provisioning.Tables[columnInfo.TableName].FilterParameters.Add(parameter);
}
i guess the error is self-explanatory.
the FilterParameters can only be set if the scope inherits from a filter template. you cannot set the FilterParameters for a normal scope, only FilterClause.
Using parameter-based filters is a two step process: Defining the filter/scope template and creating a scope based on a template.
I suggest you re-read the blog entry again and jump to the section Parameter-based Filters.