How to patch an attribute value with a Sitecore Include file - sitecore

I need to create a Sitecore include patch file to add a string to the existing value attribute of the IgnoreUrlPrefixes setting in the web.config.
I have tried to overwriting the default ignored prefixes entirely with the following include file:
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<settings>
<setting name="IgnoreUrlPrefixes">
<patch:attribute name="value">/foo/|/sitecore/default.aspx|/trace.axd|/webresource.axd|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.DialogHandler.aspx|/sitecore/shell/applications/content manager/telerik.web.ui.dialoghandler.aspx|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.SpellCheckHandler.axd|/Telerik.Web.UI.WebResource.axd|/sitecore/admin/upgrade/|/layouts/testing</patch:attribute>
</setting>
</settings>
</sitecore>
</configuration>
</settings>
Where /foo/ is the url prefix that I would like to add to the default prefixes. ShowConfig.aspx identifies that the modified configuration has not been applied.
Ideally I would like to be able to simply add /foo/ to whatever exists as the default IgnoreUrlPrefixes values. Does anyone know if this is possible and how to specify it in Sitecore patch syntax?

Good explanation of all possibilities of Sitecore include config files can be found in this John West blog post.
As you can find in the linked post:
patch:attribute: Define or replace the specified attribute.
It does not allow to "add /foo/ to whatever exists as the default IgnoreUrlPrefixes" attribute.

I recently ran into this same issue and it seems like Mark Ursino posted a blog on this particular issue:
http://firebreaksice.com/sitecore-patchable-ignore-lists/
In his example, he executes a custom pipeline after the default Sitecore one to update the value.
So instead, I’ve created a new pipeline processor that comes after the
built-in one (which will support the existing native IgnoreUrlPrefixes
setting) and will allow you to add each path via its own XML config
node. The advantage here is you can patch and continue to patch on
without needing to copy the existing values.
Sample patch file:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<httpRequestBegin>
<processor type="Sitecore.PatchableIgnoreList.ProcessPatchedIgnores, Sitecore.PatchableIgnoreList"
patch:after="processor[#type='Sitecore.Pipelines.HttpRequest.IgnoreList, Sitecore.Kernel']">
<Paths hint="list:AddPaths">
<foo>/foo</foo>
<bar>/bar</bar>
</Paths>
</processor>
</httpRequestBegin>
</pipelines>
</sitecore>
</configuration>
Source code for the pipeline processor, from the blog:
using Sitecore.Collections;
using Sitecore.Pipelines.HttpRequest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Sitecore.PatchableIgnoreList
{
public class ProcessPatchedIgnores : HttpRequestProcessor
{
private List<string> _paths = new List<string>();
public override void Process(HttpRequestArgs args)
{
string filePath = args.Url.FilePath;
foreach (string path in _paths)
{
if (filePath.StartsWith(path, StringComparison.OrdinalIgnoreCase))
{
args.AbortPipeline();
return;
}
}
}
public void AddPaths(string path)
{
if (!string.IsNullOrEmpty(path) && !_paths.Contains(path))
{
_paths.Add(path);
}
}
}
}

Related

Adding reference assemblies to Roslyn analyzer code fix unit tests

I'm attempting to write a unit test to test a Roslyn analyzer code fix. Things have moved on since the introduction of analyzers and editing DiagnosticVerifier.Helper.cs is no longer the way ( https://www.productiverage.com/creating-a-c-sharp-roslyn-analyser-for-beginners-by-a-beginner )
My analyzer works on mvc ControllerBase derived types yet adding the name of the AspNetCore assemblies to the reference assemblies does not resolve the issue of my test not resolving the AspNetCore namespace includes in the test source code
var test = new VerifyCS.Test();
mytest.ReferenceAssemblies = test.ReferenceAssemblies.AddAssemblies( ImmutableArray.Create(new string[] { "Microsoft.AspNetCore.Mvc"}));
error CS0234: The type or namespace name 'AspNetCore' does not exist
in the namespace 'Microsoft' (are you missing an assembly reference?)
EDIT:
fixed, using:
mytest.ReferenceAssemblies = mytest.ReferenceAssemblies.WithPackages(ImmutableArray.Create(new PackageIdentity[] { new PackageIdentity("Microsoft.AspNetCore.Mvc.Core", "2.2.5") }));
You need to use the long syntax of VerifyCS.Test:
await new VerifyCS.Test
{
ReferenceAssemblies = referenceAssemblies,
TestState =
{
Sources = {test },
//ExpectedDiagnostics = {VerifyCS.Diagnostic().WithLocation(0).WithArguments("xxx")}
}, // FixedCode = "yyy", etc.
}.RunAsync();
You can add assemblies and nuget packages:
var referenceAssemblies = ReferenceAssemblies.Default
.AddPackages(ImmutableArray.Create(
new PackageIdentity("serilog", "2.10.0"),
new PackageIdentity("Othe.Package.Name", "1.2.0")
)
).AddAssemblies(
ImmutableArray.Create(
"Microsoft.Extensions.DependencyInjection.Abstractions",
"Microsoft.Extensions.Hosting",
"Microsoft.Extensions.Hosting.Abstractions")
);
BEWARE: the nuget packages are recovered only from the feeds specified in nuget.config, so, if you're using pacakages from an special feed, like your company's DevOps feed, you need to include it in this config file:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
....
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="myCompanyFeed" value="https://mycompanyfeed.pkgs.visualstudio.com/_packaging/MyCoNuget/nuget/v3/index.json" />
</packageSources>
<disabledPackageSources>
<clear />
</disabledPackageSources>
</configuration>
Removing the <clear/> is another option, but less portable.

Extending the RenderField.GetFieldValue processor - not all items associated with the page are processed

I have a rich text field that has certain tokens ({{alt}} and {{title}}) for embedded images in the HTML. I extended the Sitecore.Pipelines.RenderField.GetFieldValue processor so that when a page is rendered on the front end, the processor will replace those tokens with the alt and title field values from the media item. Unfortunately my custom processor only processes a certain set of items (specifically WFFM fields), it does not process my Body field which has the tokens, even though I know the page I am browsing is rendering that field (the code run on the layout is #Html.Sitecore().Field("Body") which means it should get processed by the pipeline, correct?)
I also tried the GetTextFieldValue and GetMemoFieldValue processors but the same items were processed. Looking for some guidance as to specifically which fields are supposed to get processed by this pipeline.
Here is my Process() function:
public void Process(RenderFieldArgs args)
{
Assert.ArgumentNotNull(args, "args");
Assert.ArgumentNotNull(args.Item, "args.Item");
Assert.ArgumentNotNull(args.GetField(), "args.GetField()");
if (args.Item.Database == Database.GetDatabase("web"))
{
if (args.GetField().Value.Contains("{{alt}}"))
{
args.GetField().Value = ReplaceAltToken(args.GetField().Value);
}
if (args.GetField().Value.Contains("{{title}}"))
{
args.GetField().Value = ReplaceTitleToken(args.GetField().Value);
}
if (args.GetField().Name == "Body")
{
// Since we're rendering the body field differently we need to expand dynamic links
args.Item.Editing.BeginEdit();
args.GetField().Value = Sitecore.Links.LinkManager.ExpandDynamicLinks(args.GetField().Value);
args.Item.Editing.EndEdit();
}
}
}
My config include file:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<renderField>
<processor
type="[Redacted].Processor.RenderField.GetFieldValueExtended, [Redacted].Processor"
patch:after="processor[#type='Sitecore.Pipelines.RenderField.GetFieldValue, Sitecore.Kernel']" />
</renderField>
</pipelines>
</sitecore>
There were multiple steps to resolving this issue. Unchecking Cacheable on all renderings was an important first step. The rest of the issues I encountered were most likely specific to my solution (not well designed and will be refactored soon).
My local site did not have the most up to date files. When I updated the files, I still ran into the Attempt to retrieve context object of type 'Sitecore.Mvc.Presentation.PageContext' from empty stack error, which I was able to workaround by calling Sitecore.Web.UI.WebControls.FieldRenderer.Render(item, "Body") in a simple GetBody() function in the model, instead of Html.Sitecore().Field("Body"). Then in the view I simply put #Html.Raw(Model.Body).
I think the .Field() helper did not work because the item associated with the field requested was unknown, so yet another workaround is using Html.Sitecore().Field("Body", item) which specifies the item directly in the parameters.

Sitecore 8: Sync a bucket item on save

I have seen how we can provide default conditions and default actions to newly created bucket items. I also know that we can create a custom rule for building path based on custom date field.
But, how can we set the item path when the date field is and the is saved.
Consider an example. We have a bucket item template say "News" which has a date field say "Release Date". We have the settings where on item creation, the item path has the creation date like "/News/2015/09/16/item1". Now, we need to have some logic where we can change the path when the "release date" of "item1" is updated and the item is Saved.
How can we update the item path when item's release date is updated and item is Saved !! do i need to implement some logic in OnItemSaved() method ?
I already went through posts on GeekHive
The simplest way to do this would be to hook into the item:saved event and sync the bucket in there. The following code is untested:
public class ItemSavedEventHandler
{
public void Execute(object sender, ItemSavedEventArgs args)
{
Assert.IsNotNull(sender, "sender is null");
Assert.IsNotNull(args, "args is null");
// WARNING: using the events disabler is not recommended in this case.
// If you do this, the path of the item in question will not be updated and will cause issues when you edit the page and try to specify rendering data sources (the locations won't resolve)
using (new EventsDisabler())
{
var parameter = args.Item;
if (!BucketManager.IsItemContainedWithinBucket(paremeter))
{
return;
}
var bucketItem = parameter.GetParentBucketItemOrParent();
if (!bucketItem.IsABucket())
{
return;
}
BucketManager.Sync(bucketItem);
}
}
}
On a bucket with a lot of items, this will considerably slow down the save process tho.
If I understood you right, you want your bucket item path to be based on date updated rather than created? Am I right with that?
If yes, that it is not going to be a straightforward thing to do. I see the following approach to implement that.
Configure your bucket to be organised by update date, not created (you mentioned you already know how to configure that behavior). Every Sitecore item derived from Standard Template should have Statistics section where is __Updated field (with two underscores a the beginning) that automatically updates on each item save by corresponding event. You should use that field.
Once done, sync all existing items to apply that bucketing items paths.
Handle item:saved event
Within item:saved event handler: unbucket that particular item and re-bucket that item again (with item:unbucket and item:bucket commands)
Your that particular item will be bucketed implementing your bucketing path rule.
Hope that helps!
Building on some of the answers, here's the most readable / performant solution for most use cases:
using Sitecore.Buckets.Extensions;
using Sitecore.Buckets.Managers;
using Sitecore.Data.Events;
using Sitecore.Diagnostics;
using System;
using Sitecore.Data.Items;
using Sitecore.Events;
namespace XXXX.Project.Web.Infrastructure.Pipelines
{
public class MoveItemIntoBucketOnSave
{
public void OnItemSaved(object sender, EventArgs args)
{
Assert.IsNotNull(sender, "sender is null");
Assert.IsNotNull(args, "args is null");
var savedItem = Event.ExtractParameter(args, 0) as Item;
if (savedItem == null || savedItem.Database.Name.ToLower() != "master" || !savedItem.IsItemBucketable())
{
return;
}
// WARNING: see update below about EventDisabler
using (new EventDisabler())
{
if (!BucketManager.IsItemContainedWithinBucket(savedItem))
{
return;
}
var bucketItem = savedItem.GetParentBucketItemOrParent();
if (!bucketItem.IsABucket())
{
return;
}
// If you want to sync the entire bucket
// BucketManager.Sync(bucketItem);
BucketManager.MoveItemIntoBucket(savedItem, bucketItem);
}
}
}
}
I'm not worried about there being any empty bucket folders after this operation since they will be cleaned up during a full bucket sync, and content authors would not typically be traversing the bucket tree as they should be using search.
Here's the config:
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<events>
<event name="item:saved">
<handler type="XXXX.Project.Web.Infrastructure.Pipelines.MoveItemIntoBucketOnSave, XXXX.Project.Web" method="OnItemSaved" />
</event>
</events>
</sitecore>
</configuration>
UPDATE: I do not recommend using the EventDisabler. If you add a new page and then try to add a rendering to the page and specify a datasource for it, the datasource locations won't resolve because Sitecore still thinks the path of newly created item is a direct child of the bucket item, rather than wherever the item was moved to within the bucket. See this question for more information.
UPDATE 2:
Note that this method will get called twice when a new bucketed item is created. You should think very carefully about what this means for you, and if you should add any other checks prior to calling any other code within this method.
You can achieve this by programmatically moving the bucketed item to the root of the bucket with the BucketManager. Doing this will force it to reevaluate the bucket rules and reorganize it:
BucketManager.MoveItemIntoBucket(bucketedItem, bucketItem);
Note that this is different from BucketManager.Sync(bucketItem) because it does not sync the whole bucket, but instead handles just the single item that was changed.
In our solutions, we typically create an item:saved event handler to do this automatically:
using Sitecore.Buckets.Managers;
using Sitecore.Buckets.Util;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Events;
using System;
using System.Text.RegularExpressions;
namespace Custom.Events.ItemSaved
{
public class ReorganizeBucketedItemInBucket
{
public void OnItemSaved(object sender, EventArgs args)
{
var bucketedItem = Event.ExtractParameter(args, 0) as Item;
// If we don't have an item or we're not saving in the master DB, ignore this save
if (bucketedItem == null || !"master".Equals(bucketedItem.Database?.Name, StringComparison.OrdinalIgnoreCase))
return;
if (!bucketedItem.TemplateID.Equals(new ID("{bucketed-item-template-id}"))) return;
var itemChanges = Event.ExtractParameter(args, 1) as ItemChanges;
// If there were no changes or the changes didn't include the date field, ignore this save
if (itemChanges == null || !itemChanges.HasFieldsChanged || !itemChanges.IsFieldModified(new ID("{field-id-of-date-field}")))
return;
Item bucketItem = bucketedItem.Axes.SelectSingleItem($"{EscapePath(bucketedItem.Paths.FullPath)}/ancestor-or-self::*[##templateid = '{{bucket-container-template-id}}']");
// If this item isn't in a bucket (or is in a bucket of another, unexpected type), ignore it
if (bucketItem == null) return;
Item parent = bucketedItem.Parent;
BucketManager.MoveItemIntoBucket(bucketedItem, bucketItem);
// Delete empty ancestor bucket folders
while (parent != null && !parent.HasChildren && parent.TemplateID == BucketConfigurationSettings.BucketTemplateId)
{
Item tempParent = parent.Parent;
parent.Delete();
parent = tempParent;
}
}
/// <summary>
/// Wraps each segment of a sitecore path with "#"'s
/// </summary>
public string EscapePath(string path)
{
return Regex.Replace(path, #"([^/]+)", "#$1#").Replace("#*#", "*");
}
}
}
And don't forget your patch config, of course:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<events>
<event name="item:saved">
<handler type="Custom.Events.ItemSaved.ReorganizeBucketedItemInBucket, Custom.Events" method="OnItemSaved"></handler>
</event>
</events>
</sitecore>
</configuration>
You'll need to implement a pipeline processor.
You can do this by adding the following into a .config file in your App_Code/Include folder.
<processors>
<saveUI>
<processor mode="on" type="Namespace.ClassName, Your.Assembly" patch:after="processor[last()]" />
</saveUI>
</processor
You'll also need to implement that class - there's nothing special about it except that it must have a public Process method with a Sitecore.Pipelines.Save.SaveArgs parameter.
namespace CustomFunctions
{
public class SaveAction
{
public void Process(SaveArgs args)
{
// There's a collection of items
// I'm not sure what the situation where there's more than one item is though.
var items = args.SavedItems;
var bucket = SomeFunctionToGetParent(items);
BucketManager.Sync(items);
}
}
}
I've never actually implemented this, but I think my code should give you an idea of how to get started - though this pipeline processor would be called every time an item is saved, so you need efficient checking to make sure that the item needs to have your bucket syncing processor used.

Magento's Observer event isn't working and using webservices inside a Observer class

I'm trying to create a dynamic product discount using values from a webservice.
I've searched some guides on the internet about this matter and I found that I needed to use checkout_cart_product_add_after and checkout_cart_update_items_after.
However, I followed some guides. Created my own module (which is visible in Magento back office: Configuration > Advanced > Modules) and a observer for this module. I didn't create anything more but it's not working. Since I can see the module in that menu, I believe the problem is on the observer/event call.
Here is the config.xml (which is inside app\code\local\namespace\MyModule\etc) for my module:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<namespace_MyModule>
<version>0.1.0</version>
</namespace_MyModule>
</modules>
<global>
<events>
<checkout_cart_product_add_after>
<observers>
<namespace_MyModule_Discount>
<class>MyModule/Observer</class>
<method>MyModulePriceChange</method>
</namespace_MyModule_Discount>
</observers>
</checkout_cart_product_add_after>
</events>
</global>
</config>
And this is my Observer (which is inside app\code\local\namespace\MyModule\Model) for my module:
<?php
class namespace_MyModule_Model_Observer
{
public function MyModulePriceChange(Varien_Event_Observer $obs)
{
// Get the quote item
$item = $obs->getQuoteItem();
// Ensure we have the parent item, if it has one
$item = ( $item->getParentItem() ? $item->getParentItem() : $item );
// Load the custom price
$price = $this->_getPriceByItem($item);
// Set the custom price
$item->setCustomPrice($price);
$item->setOriginalCustomPrice($price);
// Enable super mode on the product.
$item->getProduct()->setIsSuperMode(true);
}
protected function _getPriceByItem(Mage_Sales_Model_Quote_Item $item)
{
$price = 4;
//use $item to determine your custom price.
return $price;
}
}
?>
Also, is it possible do call soap client to use a webservice inside a observer?
I hope my question is clear, thank you in advance for helping.
I see some issues with your config.xml. First of all, use capitalized both company name and module name. namespace_MyModule will become your namespace.
You have to declare models under global section like this:
<models>
<mycompany_mymodule>
<class>Mycompany_Mymodule_Model</class>
</mycompany_mymodule>
</models>
This will tell magento you want to use mycompany_mymodule for models in your module, and class name of each module will start with Mycompany_Mymodule_Model.
Where Mycompany and Mymodule are respective to folders of your module: app/code/local/Mycompany/Mymodule.
The modules section of config.xml should also have this namespace (Mycompany_Mymodule), matching name of your file app/etc/modules and folder structure in app/code/local.
The observers then become the following (I added type, and changed class):
<observers>
<namespace_MyModule_Discount>
<type>singleton</type>
<class>mycompany_mymodule/Observer</class>
<method>MyModulePriceChange</method>
</namespace_MyModule_Discount>
</observers>
Then try to test your observer method by adding there some code like die("message").
You haven't declared the models tag in the config.xml file.
An observer is a model after all and magento will not know where to find it (that MyModule/Observer you reference). Below it's an example of declaring models tag:
<models>
<MyModule>
<class>Namespace_Modulename_Model</class>
</MyModule>
</models>
Yes, you can do soap api calls inside observer.

how replace XmlGregorianCalendar by Date?

I have to expose an ejb service layer via jax-ws .
I have generated the web service using jax-ws and wsimport but I'm stopped by a strange things ; Date are being mapped to XmlGregorianCalendar .
Is it possible to use classic java Date instead ?
Can you show me the right way to proceed ?
Thanks .
Edit:
this the binding file i used :
thanks , I modified slightly your xml and attached it with netbeans to the client's webservice and it worked . This the binding I used :
<jaxws:bindings node="wsdl:definitions/wsdl:types/xsd:schema"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" wsdlLocation="../wsdl/localhost_8080/web_test/Testor.wsdl" >
<jaxb:globalBindings>
<jaxb:javaType name="java.util.Date"
xmlType="xsd:dateTime"
parseMethod="lol.XsdDateTimeConverter.unmarshal"
printMethod="lol.XsdDateTimeConverter.marshalDateTime"
/><jaxb:javaType
name="java.util.Date"
xmlType="xsd:date"
parseMethod="lol.XsdDateTimeConverter.unmarshal"
printMethod="lol.XsdDateTimeConverter.marshalDate"
/>
</jaxb:globalBindings>
</jaxws:bindings>
Not tested, but should work. First create such class:
import javax.xml.bind.DatatypeConverter;
public class XsdDateTimeConverter {
public static Date unmarshal(String dateTime) {
return DatatypeConverter.parseDate(dateTime).getTime();
}
public static String marshalDate(Date date) {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(date);
return DatatypeConverter.printDate(calendar);
}
public static String marshalDateTime(Date dateTime) {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(dateTime);
return DatatypeConverter.printDateTime(calendar);
}
}
Then add this to custom xjb file:
<javaType
name="java.util.Date"
xmlType="xs:dateTime"
parseMethod="XsdDateTimeConverter.unmarshal"
printMethod="XsdDateTimeConverter.marshalDateTime"
/>
<javaType
name="java.util.Date"
xmlType="xs:date"
parseMethod="XsdDateTimeConverter.unmarshal"
printMethod="XsdDateTimeConverter.marshalDate"
/>
</globalBindings>
Not tested, but should work. Based on my answer here: JAX-WS and Joda-Time?
Thanks Tomasz. The above solution works.
But wsimport also adds its set of Adapters like Adapter1.java and Adapter2.java with its package org.w3._2001.xmlschema, which really doesnot match my own package structure.
I found a way to change this package name using another jaxb binding. Actually, I searched for this a lot and could not find this easily, so I am adding it here for anyone looking for the same.
Add the following binding in the wsimport using '-b binding.xml'. Note that wsimport can work with multiple binding files.
binding.xml content below:
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0">
<annotation><appinfo>
<jaxb:schemaBindings>
<jaxb:package name="com.abc.xyz.utils"/>
</jaxb:schemaBindings>
</appinfo></annotation>
</schema>