Versioning for ContentPart's without a record - orchardcms

Is it possible to create draftable versions of content parts that have no record.
For example
public MyContentPart : ContentPart
{
...
}
When I create a draftable version of MyContentPart it doesn't behave the same as a part that inherits from ContentPart<MyContentPartVersionRecord> would. E.g. change some values of it's properties and still see the original values in the last published version.

Related

Defaulting user-defined fields for records created before the customization

I have a DAC extension that has a default attribute set for it(see definition below). I have noticed that for any records that existed prior to the DAC extensions existence will still hold a null value. Is there a conventional way that we can get all the records initialized?
If not I will likely perform a check within an event handler before its used.
Thanks in advance!
public abstract class usrCustOptInOut : IBqlField
{
}
protected string _UsrCustOptInOut;
[PXDBString(1, IsUnicode = true)]
[PXDefault(OptInOut.Default, PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Click To Pay Opt In/Out")]
[PXStringList(
new[]
{
OptInOut.OptOut,
OptInOut.OptIn,
OptInOut.Default
},
new[]
{
"Customer Opt Out",
"Customer Opt In",
"Default"
})]
public virtual string UsrCustOptInOut
{
get
{
return _UsrCustOptInOut;
}
set
{
_UsrCustOptInOut = value;
}
}
You will need to run an update manually. Existing records will not be updated automatically when adding new fields and records exist.
I see two options:
Add a a customization plugin to your project (preferred)
Add a custom SQL script to your project
Option 1
You can use a customization plugin to make sure all null values have a default. The customization plugin will run at the end of each publish within the site.
You can create a code file in your customization project to this:
The UpdateDatabase() method in a CustomizationPlugin runs after the customization was published and the website was restarted (the Usr field will be applied).
Then you can use PXDatabase.Update to run the update without needing a graph or any validation/events to run. You just need a bulk SQL update and PXDatabase.Update will do the job. The example below will only set a default if there is a null value. The same concept can be applied to any table/field added not just user fields. For example if you have a custom table and you need to add a new field and have existing records contain a default.
A down side (when compared to Option 2) is PXDatabase.Update will run for the current tenant (company) as it appends the current CompanyID to the SQL statement (which is a good thing). If you have multiple tenants (companies) you will need to run the publish for multiple tenants. Alternativly you can write your code so that it will loop each company and run your statement using PXLoginScope (see references).
The end result using a Customization Plugin would look something like this:
public class UsrFieldDefaults : CustomizationPlugin
{
//This method executed after customization was published and website was restarted.
public override void UpdateDatabase()
{
PXDatabase.Update<MyDac>(
new PXDataFieldAssign<MyDacExt.usrCustOptInOut>(PXDbType.NChar, 1, OptInOut.Default),
new PXDataFieldRestrict<MyDacExt.usrCustOptInOut>(PXDbType.NChar, 1, null, PXComp.ISNULL)
);
}
}
Option 2
You can write a custom SQL script to do the same and put it in your customization project. Keep in mind which DBMS you are publishing to (MSSQL/MYSQL). There is also a way to use a shared SQL syntax so it should work for any DBMS. Also be aware that you would most likely run the script for all tenants (companies) which would include snapshots.
Additional References:
Custom Processes During Publication of a Customization
To Add a Customization Plug-In to a Project
Save data to different company
To Publish a Customization for a Multitenant Site
Creating a Custom SQL Script
Writing Custom SQL Scripts for Interpretation

Make a Field Mandatory on the Graph Level

Newbie to Acumatica here. I've performed a small amount of customization to our system, and am now diving into adding custom data fields.
My goal is to synchronize hardware shipment information from Acumatica into our legacy (outdated and proprietary) hardware management system, as we will need to continue using this system for the time being for warranty calculations. I plan to eventually build this into Acumatica.
My current issue is that I need a method of associating Customer Locations to the customer locations in our legacy system. Adding the field DCL_ID was easy enough to accomplish following the To Add a Custom Data Field documentation. I made the column be required by setting
[PXDefault]
[PXUIField(DisplayName="DCL Account ID", Required = true)]
to the attributes section of the Data Access class as outlined here. I then added the field to my form using the Layout Editor.
At this point all seemed well. The field shows an asterisk in the UI and also validates that a value is provided. Then I realized that Customer Locations is not the only place that uses CR.Location -- it is also used by Account Locations. Doing some digging I've found that Account Locations can include many more account types than Customer Locations. I only need this attribute to be required for Customer Locations. Thus, I have opted to use the To Make a Field Mandatory on the Graph Level.
Here is my CustomerLocationMaint code:
using System;
using PX.Data;
using PX.Objects.CR;
using System.Collections.Generic;
using PX.Objects;
using PX.Objects.AR;
namespace PX.Objects.AR
{
public class CustomerLocationMaint_Extension : PXGraphExtension<CustomerLocationMaint>
{
#region Event Handlers
[PXDefault]
[PXCustomizeBaseAttribute(typeof(PXUIFieldAttribute), "Required", true)]
protected virtual void SelectedCustomerLocation_UsrDCL_ID_CacheAttached(PXCache cache)
{
}
#endregion
}
}
After I save and publish the customization, the field does not function as a required field, as it did when I defined the requirements at the DAC level.
So, what have I done wrong? I've read and re-read the documentation multiple times, but cannot find my mistake.
Setup:
My thought is the underscore in the field name causing the cache attached to not properly register the graph level attribute change. Using a field name without the underscore is the preferred naming convention for tables and columns.
The Acumatica documentation mentions this should be avoided as listed here:
Database Design Guidelines
Found under Table and Column Naming Conventions:
Do not use the underscore symbol (_) in table or column names, because
it is a reserved symbol in Acumatica Framework. For example,
CompanyType is a valid column name, while Company_Type is invalid.

E-Commerce Product Custom Field Kentico 10

I am using E-commerce module of Kentico portal and it has two fields for Products pricing : SKUPrice and SKURetailPrice.
I needed one more field to show sell price and I added a new field in Modules application of the portal.(Modules->E-Commerce->Classes->SKU->Fields->New Field)
Now, I need to access this field in my code,but SKUInfo class doesn't show me the newly added field.What I need to do so that the newly added field reflects in my project code ?
I have already build the entire solution multiple times.Any other solutions please.
You can use GetValue and SetValue methods for such fields like this:
SKUInfo sku = ...;
string a = sku.GetValue("field").ToString();
sku.SetValue("field", "value");
JanH has the answer for custom fields you set, also keep in mind though that there is a "SKUCustomData" for other information that you want to store. It takes a Name-Value pair dictionary if memory serves me correctly, and useful if you need to store configuration information or other things that won't be located on the normal SKU table.

Customized Tridion Search Index Handler: Custom vs Standard field for page url?

I was playing around with custom Search Indexing Handlers for SDL Tridion 2011 (GA). I got something working, using the very helpful information provided by Arjen, however I am not sure if my execution is the best option.
The requirement is to be able to search for pages in the CMS by url (eg www.example.com/news/index.html). In order to do this I have the created a class using the ISearchIndexingHandler interface (code below). I am indexing the url in the ContentText field of the item, however I am not sure if this would normally contain something else for a page (I think a page only has metadata so this should be OK). The advantage of using this over a custom field is that I can simply type the url in the search box without having to use <url> IN <fieldname> or something like that.
So my question is, is there any reason not to use ContentText for Pages, and is there any advantage in using a custom field? Also bonus marks go to anyone with good ideas on how to handle BluePrinting (if I create a page in a parent publication, I want the local urls also to be indexed in the child publications), and the case where a Structure group path is altered (I guess I can somehow trigger a re-index of child page items from within my indexing handler).
The code:
using System;
using Tridion.ContentManager.Search;
using Tridion.ContentManager.Search.Indexing.Handling;
using Tridion.ContentManager.Search.Indexing.Service;
using Tridion.ContentManager.Search.Indexing;
using Tridion.ContentManager.Search.Fields;
namespace ExampleSearchIndexHandler
{
public class PageUrlHandler : ISearchIndexingHandler
{
public void Configure(SearchIndexingHandlerSettings settings)
{
}
public void ExtractIndexFields(IdentifiableObjectData subjectData, Item item, CoreServiceProxy serviceProxy)
{
PageData data = subjectData as PageData;
if (data != null)
{
PublishLocationInfo info = data.LocationInfo as PublishLocationInfo;
string url = GetUrlPrefix(data) + info.PublishLocationUrl;
item.ContentText = url;
}
}
private string GetUrlPrefix(PageData page)
{
//hardcoded for now, but will be read from publication metadata
return "www.example.com";
}
}
}
You can store the url in the ContextText Property. Thies field is used to index Template content data.
Tridion does not index shared item(s) of child publication.
Indexing is triggered on Item modification(create, update, delete, localize and unlocalize).
Or you can use reindexing tool to reindex ur item. but there is no way to index shared items in child publication.
I don't think you can include the URL prefix in neither your search query as the indexed item. Because shared items are not indexed, you will probably index the Page from the Website Structure layer, which is never published.
When a Structure Group is moved you would have to make an event handler that triggers re-indexing all child pages using a protected method of the TOM.NET API. This method is not part of the public API, so posting the code for that solution would probably declare me a persona non grata with R&D :)
Before you re-index anything you should store the original publish location url of the Structure Group in the TcmEventArgs.ContextVariables property, so you can verify whether or not a re-indexing action is necessary.

Adding nodes to mvcsitemap in code

I am building a simple CMS that stores the navigation/sitemap/site structure in a database table. When the app starts up I can create my sitemap nodes dynamically based on the data from that table.
The problem I am having is when a new record is added to the table or an existing one is updated while the app is running I need to rebuild the sitemap structure. Seems simple enough but I cannot figure out how.
There is no built-in support of clearing/rebuilding the sitemap on-the-fly.
But you could implement this yourself quite easily by implementing a custom sitemapprovider deriving from MvcSiteMapProvider.DefaultSiteMapProvider, and create a method used to clear the cache, using the protected Clear() method. Example:
public class CustomSiteMapProvider : DefaultSiteMapProvider
{
public void ClearSiteMap()
{
Clear();
}
}
I haven't tried it but it should work. Remember to changed the web.config file to use the custom sitemapprovider instead of DefaultSiteMapProvider.

Resources