I am having an issue while setting default value for datetime field in sitecore standard values.
I know that $date takes the current date. If I specify $date in standard values for a date time field , it always takes the date as "1/1/0001".
How do I fix this ?
It is possible to enter tokens in the fields on the standard values, and then these will be replaced with other values, but only when a new item which use that template is created. It will not set date for the existing items which use this template.
$date is one of the token and it's replates with the system date (yyyyMMdd).
There is a blog post written by John West which explains how to Expand Standard Values Tokens in Existing Items with the Sitecore ASP.NET CMS.
EDIT:
Here is the code which is a part of MasterVariablesReplacer class which is used to replace $date token:
text = this.ReplaceWithDefault(text, "$date", (Func<string>) (() => DateUtil.IsoNowDate), context);
It is called from the ReplaceVariables processor, which is a part of expandInitialFieldValue pipeline (see /sitecore/admin/showconfig.aspx for all the expandInitialFieldValue processors).
You can try to add your own processor to this pipeline and see why the $date is not replaced properly:
public class ReplaceVariables : ExpandInitialFieldValueProcessor
{
public override void Process(ExpandInitialFieldValueArgs args)
{
Assert.ArgumentNotNull((object) args, "args");
MasterVariablesReplacer variablesReplacer = Factory.GetMasterVariablesReplacer();
string text = args.SourceField.Value;
if (variablesReplacer == null)
args.Result = text;
else
args.Result = variablesReplacer.Replace(text, args.TargetItem);
}
}
Related
Scenario: Deactivate the user whose login date is less than 42 from today. I have an user whose last login date is 1/22/2020(US Date format)/22/1/2020 5:12 pm. Here I wrote a batch apex for deactivating. My code has executed successfully and my batch status is completed but the user record is not deactivating.
Here is the code:
global class User_Deactivation implements Database.Batchable<SObject>
{
dateTime dt = date.today()-42;
public String query = 'SELECT Name, LastLoginDate, Id From User WHERE IsActive = true AND LastLoginDate=:dt ';
global Database.querylocator start(Database.BatchableContext bc)
{
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext bc,List<User> scope)
{
List<User> userList = new List<User>();
for(User s:scope)
{
User u =(user)s;
userList.add(u);
}
if(userList.size() > 0)
{
for(User usr : userList)
{
usr.isActive = false;
}
}
update userList;
}
global void finish(Database.BatchableContext bc)
{
AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email
FROM AsyncApexJob
WHERE Id = :BC.getJobId()];
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {a.CreatedBy.Email};
mail.setToAddresses(toAddresses);
mail.setSubject('Apex Job Status: ' + a.Status);
mail.setPlainTextBody('The batch Apex job processed ' + a.TotalJobItems + ' batches with '+ a.NumberOfErrors + ' failures.');
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
}
please help me out on this
Multiple things you can improve here, where do I begin...
Initialisation(?) piece
dateTime dt = date.today()-42;
String query = 'SELECT Name, LastLoginDate, Id From User WHERE IsActive = true AND LastLoginDate=:dt';
Do you need Date or DateTime match? The way you wrote it it'll match only people who logged in exactly at midnight. System.debug(dt); would say 2020-01-23T00:00:00.000Z. It shouldn't be an equals sign, should be "less than" or "less or equal".
Or even better - you can make it bit more clear what you want to do, bit more "semantic" so the poor guy who's going to maintain it can understand it without extra comments. This reads more natural and uses the SOQL date literals, special "constants" to simplify your logic: SELECT Id, LastLoginDate FROM User WHERE isActive = true AND LastLoginDate != LAST_N_DAYS:42
What is this section of code anyway. It's not really static variables, it's not a constructor... I think it'll behave as a constructor. Be very, very careful with constructors for batches. The state of the class at the end of the constructor gets saved (serialised) and restored every time the class is scheduled to run. It's tempting to put some initialisation code into constructor, maybe read some custom settings, precalculate stuff... But then you'll be in for nasty surprise when admin adds new custom setting and the batch doesn't pick it up. In your case it's even worse, I'd suspect it'll serialise the dt and your today() will be frozen in time, not what you expected. To be safe move all initialisation logic to start()
And I'd even say whoever gave you the requirement didn't think it through. When you make new user they get a link they need to click in next 72h. If they didn't do it (maybe it was sent late Friday and they want to login on Monday) - this thing will dutifully kill their access at Friday night without giving them any chance to login. You need some "grace period". Maybe something like WHERE isActive = true AND (LastLoginDate < :x OR (LastLoginDate = null AND CreatedDate < :x))
start()
Queries in strings work and that's how a lot of batch documentation is written but they are poor practice. Where possible use a compiled query, in brackets. You get minimal improvement in execution (precompiled), you get compile-time warnings when you mess up (better than a runtime error which you might not notice if you don't monitor jobs). And most importantly - if somebody wants to delete a field - SF will detect a dependency and stop him/her. Use return Database.getQueryLocator([SELECT ...]); wherever you can.
execute()
Your scope already is a list of users, why do you do extra casts to User? Why do you add them to a helper list? Why 2 loops?
for(User u : scope){
u.isActive = false;
}
update users;
and you're done?
P.S. Why "global" all over the place?
I'm trying to figure out how to get the path to an image from an entity in Drupal 8. I had thought get()->value would do it, but that just returns a blank string.
I have a test function:
function getValueTest ($profile_id, $field)
{
$profile_storage = \Drupal::entityManager()->getStorage('profile');
$profile = $profile_storage->load($profile_id);
if ($profile != null)
{
if ($profile->hasField ($field))
{
return $profile->get ($field)->value;
}
}
return "No field" . $field;
}
Assume some profile id 3 that has two fields field_first_name and field_mugshot. If I call:
dpm ($this->getValueTest (3, 'field_first_name'));
dpm ($this->getValueTest (3, 'field_mugshot'));
The first call correctly displays the first name in the message area, but the second just gives a blank string. I need a path to the image so I can do some processing on its content.
You can use the folowing methods to get the uri or the url:
$entity->get('field_image')->entity->getFileUri();
$entity->get('field_image')->entity->url();
This is because value() method should return the value of the field(aka a fid), field which is an entity reference in this case.
In my .NET application, I recently had to make some changes in the database structure and upon changing code I have run into this error message.
The line used to say _categoryID = new Guid(Request.QueryString["CategoryID"].ToString()); which worked fine to retrieve a list of products based on the categoryid, but now I had to add a top level category called Market, and I used int instead of Guid in the database, because to me using Guid is a pain.
But now when I change the line I mentioned to _marketID = new Int32(Request.QueryString["MarketID"].ToString()); I get the error.
Here is the chunk of code :
#region Variables
Int32 _marketID;
#endregion
if ( Request.QueryString [ "MarketID" ] != null )
{
_marketID = new Int32(Request.QueryString["MarketID"].ToString());
ViewState["MarketID"] = _marketID;
BindDataToUI ( );
CreateFilterInSession ( );
}
Try this instead :
_marketID = Convert.ToInt32(Request.QueryString["MarketID"]);
note : no need to use ToString() for querystring values, they're all natively strings anyway.
Hey all, I was able to do this via a SELECT CASE statement, however I'm always trying to improve my code writing and was wondering if there was a better approach. Here's the scenario:
Each document has x custom fields on it.
There's y number of documents
However there's only 21 distinct custom fields, but they can obviously have n different combinations of them depending on the form.
So here's what I did, I created an object called CustomFields like so:
Private Class CustomFields
Public agentaddress As String
Public agentattorney As String
Public agentcity As String
Public agentname As String
Public agentnumber As String
Public agentstate As String
Public agentzip As String
... more fields here ....
End Class`
Then I went ahead and assigned the values I get from the user to each of those fields like so:
Set All of Our Custom Fields Accordingly
Dim pcc As New CustomFields()
pcc.agentaddress = agent.address1
pcc.agentattorney = cplinfo.attorneyname
pcc.agentcity = agent.city
pcc.agentname = agent.agencyName
pcc.agentnumber = agent.agentNumber
pcc.agentstate = agent.state
pcc.agentzip = agent.zip ....other values set to fields etc.
Now the idea is based upon what combo of fields come back based upon the document, we need to assign the value which matches up with that custom field's value. So if the form only needed agentaddress and agentcity:
'Now Let's Loop Through the Custom Fields for This Document
For Each cf As vCustomField In cc
Dim customs As New tblCustomValue()
Select Case cf.fieldname
Case "agentaddress"
customs.customfieldid = cf.customfieldid
customs.icsid = cpl.icsID
customs.value = pcc.additionalinfo
Case "agentcity"
customs.customfieldid = cf.customfieldid
customs.icsid = cpl.icsID
customs.value = pcc.additionalinfo
End Select
_db.tblCustomValues.InsertOnSubmit(customs)
_db.SubmitChanges()
This works, however we may end up having 100's of fields in the future so there a way to somehow "EVAL" (yes I know that doesn't exist in vb.net) the cf.fieldname and find it's corresponding value in the CustomFields object?
Just trying to write more efficient code and looking for some brainstorming here. Hopefully my code and description makes sense. If it doesn't let me know and I'll go hit my head up against the wall and try writing it again.
If I am reading your question correctly, you are trying to avoid setting the value of fields, when the field isn't used. If so, I would recommend you just go ahead and set the field to nothing in that case.
I have a requirement to specify some values per-item per-sale. Imagine being able to add a gift message to each item in the basket individually.
How can this be achieved?
I'm using nopCommerce 1.6 (for .net 3.5 compatibility).
I have added three "Product Attributes" (Catalog > Products > Product Attributes). Created a product and in the default product variation, added the three attributes to the product.
The attributes are of type TextBox which, I believe will allow me to enter any value I like as a string.
How do I programatically set these values. From what I can tell ShoppingCartManager.AddToCart looks like it takes a string containing XML for the attributes as the fourth argument:
public static List<string> AddToCart(ShoppingCartTypeEnum shoppingCartType, int productVariantId, string selectedAttributes, decimal customerEnteredPrice, int quantity);
But I can't see anything that explains how the XML should be structured.
Please note: I'm integrating with another CMS so I'm not using the standard nopCommerce controls for the display of the products.
To manually set the value of product attributes on a product variant you can use the helper methods found in :
NopSolutions.NopCommerce.BusinessLogic.Products.ProductManager
NopSolutions.NopCommerce.BusinessLogic.Products.Attributes.ProductAttributeManager
NopSolutions.NopCommerce.BusinessLogic.Products.Attributes.ProductAttributeHelper
NopSolutions.NopCommerce.BusinessLogic.Orders.ShoppingCartManager
(this presumes your project is based on the normal nopCommerce example site.)
The process is fairly straight forward however; I assume the product attributes are of type TextBox in the nopCommerce catalog. This allows any string to be set as the value of the attribute.
Overview of process
Get the product variant, this assumes you already know the product Id and which variant of the product you want (if you have more than one).
Get the attributes for the variant.
Use ProductAttributeHelper to generate your attribute XML string
Save the product to the cart with these attributes.
Example code
private bool SaveProductToBasket()
{
var product = GetTheProduct();
int productId = product.ProductId;
var variants = ProductManager.GetProductVariantsByProductId(productId);
int variantId = GetDesiredVariantId();
var variant = variants[variantId];
var attributes =
ProductAttributeManager.GetProductVariantAttributesByProductVariantId(variant.ProductVariantId);
string data = string.Empty;
data = SetVariantAttribute(data, attributes, "Attribute1", value1.ToString());
data = SetVariantAttribute(data, attributes, "Attribute2", value2.ToString());
data = SetVariantAttribute(data, attributes, "Attributee", value3.ToString());
var addToCartWarnings =
ShoppingCartManager.AddToCart(ShoppingCartTypeEnum.ShoppingCart, variant.ProductVariantId, data, decimal.Zero, 1);
if (addToCartWarnings.Count == 0)
{
return true;
}
// TODO: Bind warnings.
return false;
}
private string SetVariantAttribute(string data, ProductVariantAttributeCollection attributes, string attributeName, string value)
{
var attribute = (from a in attributes
where a.ProductAttribute.Name == attributeName
select a).First();
return ProductAttributeHelper.AddProductAttribute(data, attribute, value);
}
Just to add to this string. The XML for the product attributes look like this...
<Attributes>
<ProductVariantAttribute ID="66">
<ProductVariantAttributeValue>
<Value>484</Value>
</ProductVariantAttributeValue>
</ProductVariantAttribute>
<ProductVariantAttribute ID="67">
<ProductVariantAttributeValue>
<Value>486</Value>
</ProductVariantAttributeValue>
</ProductVariantAttribute>
</Attributes>