In an NVelocity template how do you test for the existence of a property - nvelocity

I'm building a simple NVelocity template but I can't figure out how to test for the existence of a variable -- In this example I want to test if the context contains a property callwed User.
I know I can implement the same functionality as a hacked foreach loop but I was wondering if there's a better way.
Velocity.Init();
VelocityContext context = new VelocityContext();
context.Put("from", "somewhere");
context.Put("to", "someone");
context.Put("subject", "Welcome to NVelocity");
String s = #"From: $from To: $to Subject:
#if($context.ContainsKey('User'))
We Have a User
#else
No User Found
#end";
var sw = new System.IO.StringWriter();
Velocity.Evaluate(context, sw, "", s);
string merged = sw.ToString();

The context itself is not a part of the context, so $context doesn't work. You can check for presence like this:
#if ($user)
we have a user
#else
no user found
#end

Related

Drupal 8 | Wrong alias used

Bonjour,
I have a problem on Drupal 8 that I can't solve, that's why I'm calling on you.
I have 2 aliases for the same node :
/public/event/10
/pro/event/10
I have a block_1 that only appears on the " /public/* " pages and a block_2 on the " /pro/* " pages.
When I access to the url "/pro/event/10", block_1 is displayed and not block_2.
I conclude that Drupal selects the alias "/public/event/10" (probably the first one he finds) while I'm on the page "/pro/event/10".
How can I programmatically tell Drupal the right alias to use?
Thank you in advance for your help.
Here is the code if it can help someone
class OOutboundPathProcessor implements OutboundPathProcessorInterface
{
function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL)
{
// Only for nodes
if (!isset($options['entity_type']) OR $options['entity_type'] !== 'node')
{
return $path;
}
// Get current 'space'
$espace = \Drupal::service('session')->get('espace');
// Get the node to process
$node = $options['entity'];
// New path
$str_path = "/%s/%s/%s";
$new_path = sprintf($str_path, $espace, $node->bundle(), $node->id());
// Check new path
$isValid = \Drupal::service('path.validator')->isValid($new_path);
if ($isValid === true) return $new_path;
return $path;
}
}
You might want to create your own path_processor_outbound service by implementing OutboundPathProcessorInterface.
This implementation may work on /node/{id} paths if the current requests path matches /public/event/** or /pro/event/**.
Analyzing the node entity for it's type (bundle): If it is event generate and return your desired path; if it is not event don't manipulate the path and return the original.
Writing the actual implementation in PHP code may be your own pleasure ;-)

XPath: get value using relative path (PHP, Symfony, DOMCrawler) error

I'm writing a test to test my understanding of XPath with Symfony's DomCrawler:
$crawler = new Crawler();
$crawler->add('<foo><foo>bar</foo></foo>');
$crawlerWithNodes = $crawler->filterXPath('/foo/foo');
$this->assertEquals('bar', $crawlerWithNodes->text());
But the above results in:
InvalidArgumentException: The current node list is empty.
On line:
$this->assertEquals('bar', $crawlerWithNodes->text());
What am I doing wrong?
You need to load the content as DOMDocument instead of simple string (is interpreted as HTML).
You also need to add the _root prefix in the xpath string if you want to find an absolute path in the structure. See the Symfony\Component\DomCrawler\Tests\CrawlerTest cass for further example (testFilterXpathComplexQueries method)
Consider the following test method:
public function testCrawlerAsHtml()
{
$crawler = new Crawler();
$crawler->add('<html><body><foo><foo>bar</foo></foo></body></html>');
//die(var_dump($crawler->html()));
$crawlerWithNodes = $crawler->filterXPath('/_root/html/body/foo/foo');
$this->assertEquals('bar', $crawlerWithNodes->text());
}
public function testCrawlerAsDomElement()
{
$dom = new \DOMDocument();
$dom->loadXML('<foo><foo>bar</foo></foo>');
$crawler = new Crawler();
$crawler->add($dom);
// die(var_dump($crawler->html()));
$crawlerWithNodes = $crawler->filterXPath('/_root/foo/foo');
$this->assertEquals('bar', $crawlerWithNodes->text());
}
Hope this help

changing the Request.QueryString value

how can i modified the querystring?
I have capture the query string like this
qs = Request.QueryString["flag"].ToString();
and then rebuilt the query string with modified values
and response.redirect(url & qs) to it
While I'm not sure I'd suggest using this approach liberally, if you wanted to reconstruct the path and query string with a few changes... you could convert the query string to an editable collection, modify it, then rebuild it from your new collection.
Goofy example...
// create dictionary (editable collection) of querystring
var qs = Request.QueryString.AllKeys
.ToDictionary(k => k, k => Request.QueryString[k]);
// modify querystring
qs["flag"] = "2";
// rebuild querystring
var redir = string.Format("{0}{1}", Request.Path,
qs.Aggregate(new StringBuilder(),
(sb, arg) => sb.AppendFormat("{0}{1}={2}",
sb.Length > 0 ? "&" : "?", arg.Key, arg.Value)));
// do something with it
Response.Redirect(redir);
While I definitely wouldn't recommend the below for production code, for testing purposes you can use reflection to make the querystring collection editable.
// Get the protected "IsReadOnly" property of the collection
System.Reflection.PropertyInfo prop = Request.QueryString.GetType()
.GetProperty("IsReadOnly", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
// Set the property false (writable)
prop.SetValue(Request.QueryString, false, null);
// Have your way with it.
Request.QueryString.Add("flag", "2");
To combine the required destination URL based on the Request’s properties, use something like this:
string destUrl = string.Format("{0}://{1}{2}/", Request.Url.Scheme, Request.Url.Authority, Request.Url.AbsolutePath);
if (destUrl.EndsWith("/"))
destUrl = destUrl.TrimEnd(new char[] { '/' });
if (!string.IsNullOrEmpty(Request.QueryString["paramName"])) {
destUrl = string.Format("{0}?paramName={1}", destUrl, "paramValueHere");
Response.Redirect(destUrl);
}
i am not sure if I understand your question. You can just alter the string qs and use.
qs = qs + "modification"
Response.Redirect("this.aspx?flag=" + qs )
The stuff in the Request class deals with the request that got you to the page. You can't edit it because the client constructed it, not the server.

NVelocity not finding the template

I'm having some difficulty with using NVelocity in an ASP.NET MVC application. I'm using it as a way of generating emails.
As far as I can make out the details I'm passing are all correct, but it fails to load the template.
Here is the code:
private const string defaultTemplatePath = "Views\\EmailTemplates\\";
...
velocityEngine = new VelocityEngine();
basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, defaultTemplatePath);
ExtendedProperties properties = new ExtendedProperties();
properties.Add(RuntimeConstants.RESOURCE_LOADER, "file");
properties.Add(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, basePath);
velocityEngine.Init(properties);
The basePath is the correct directory, I've pasted the value into explorer to ensure it is correct.
if (!velocityEngine.TemplateExists(name))
throw new InvalidOperationException(string.Format("Could not find a template named '{0}'", name));
Template result = velocityEngine.GetTemplate(name);
'name' above is a valid filename in the folder defined as basePath above. However, TemplateExists returns false. If I comment that conditional out and let it fail on the GetTemplate method call the stack trace looks like this:
at NVelocity.Runtime.Resource.ResourceManagerImpl.LoadResource(String resourceName, ResourceType resourceType, String encoding)
at NVelocity.Runtime.Resource.ResourceManagerImpl.GetResource(String resourceName, ResourceType resourceType, String encoding)
at NVelocity.Runtime.RuntimeInstance.GetTemplate(String name, String encoding)
at NVelocity.Runtime.RuntimeInstance.GetTemplate(String name)
at NVelocity.App.VelocityEngine.GetTemplate(String name)
...
I'm now at a bit of an impasse. I feel that the answer is blindingly obvious, but I just can't seem to see it at the moment.
Have you considered using Castle's NVelocityTemplateEngine?
Download from the "TemplateEngine Component 1.1 - September 29th, 2009" section and reference the following assemblies:
using Castle.Components.Common.TemplateEngine.NVelocityTemplateEngine;
using Castle.Components.Common.TemplateEngine;
Then you can simply call:
using (var writer = new StringWriter())
{
_templateEngine.Process(data, string.Empty, writer, _templateContents);
return writer.ToString();
}
Where:
_templateEngine is your NVelocityTemplateEngine
data is your Dictionary of information (I'm using a Dictionary to enable me to access objects by a key ($objectKeyName) in my template.
_templateContents is the actual template string itself.
I hope this is of help to you!
Just to add, you'll want to put that into a static method returning a string of course!
Had this issue recently - NVelocity needs to be initialised with the location of the template files. In this case mergeValues is an anonymous type so in my template I can just refer to $Values.SomeItem:
private string Merge(Object mergeValues)
{
var velocity = new VelocityEngine();
var props = new ExtendedProperties();
props.AddProperty("file.resource.loader.path", #"D:\Path\To\Templates");
velocity.Init(props);
var template = velocity.GetTemplate("MailTemplate.vm");
var context = new VelocityContext();
context.Put("Values", mergeValues);
using (var writer = new StringWriter())
{
template.Merge(context, writer);
return writer.ToString();
}
}
Try setting the file.resource.loader.path
http://weblogs.asp.net/george_v_reilly/archive/2007/03/06/img-srchttpwwwcodegenerationnetlogosnveloc.aspx
Okay - So I'm managed to get something working but it is a bit of a hack and isn't anywhere near a solution that I want, but it got something working.
Basically, I manually load in the template into a string then pass that string to the velocityEngine.Evaluate() method which writes the result into the the given StringWriter. The side effect of this is that the #parse instructions in the template don't work because it still cannot find the files.
using (StringWriter writer = new StringWriter())
{
velocityEngine.Evaluate(context, writer, templateName, template);
return writer.ToString();
}
In the code above templateName is irrelevant as it isn't used. template is the string that contains the entire template that has been pre-loaded from disk.
I'd still appreciate any better solutions as I really don't like this.
The tests are the ultimate authority:
http://fisheye2.atlassian.com/browse/castleproject/NVelocity/trunk/src/NVelocity.Tests/Test/ParserTest.cs?r=6005#l122
Or you could use the TemplateEngine component which is a thin wrapper around NVelocity that makes things easier.

NVelocity -- #parse with embedded resources

I'm generating emails based off embedded NVelocity templates and would like to do something with dynamically included sections. So my embedded resources are something like this:
DigestMail.vm
_Document.vm
_ActionItem.vm
_Event.vm
My email routine will get a list of objects and will pass each of these along with the proper view to DigestMail.vm:
public struct ItemAndView
{
public string View;
public object Item;
}
private void GenerateWeeklyEmail(INewItems[] newestItems)
{
IList<ItemAndView> itemAndViews = new List<ItemAndView>();
foreach (var item in newestItems)
{
itemAndViews.Add(new ItemAndView
{
View = string.Format("MyAssembly.MailTemplates._{0}.vm", item.GetType().Name),
Item = item
});
}
var context = new Dictionary<string, object>();
context["Recipient"] = _user;
context["Items"] = itemAndViews;
string mailBody = _templater.Merge("MyAssembly.MailTemplates.DigestMail.vm", context);
}
And in my DigestMail.vm template I've got something like this:
#foreach($Item in $Items)
====================================================================
#parse($Item.viewname)
#end
But it's unable to #parse when given the path to an embedded resource like this. Is there any way I can tell it to parse each of these embedded templates?
Hey Jake, is .viewname a property? I'm not seeing you setting it in your code, how about you use the following:
#foreach($Item in $Items)
====================================================================
$Item.viewname
#end
I don't know why you're parsing the $Item.viename rather than just using the above? I'm suggesting this as I've just never needed to parse anything!
Please refer to this post where we've discussed the generation of templates.
Hope this helps!

Resources