I am trying to install one from two features based on the value that should be set inside of the custom action.
Firstly, I set the value of a property:
UINT __stdcall ConfigurationCheckAction(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_INSTALL_FAILURE;
hr = WcaInitialize(hInstall, "ConfigurationCheckAction");
if (condition) {
MsiSetProperty( hInstall, TEXT("STREAM"), TEXT("RED") );
}
else {
MsiSetProperty( hInstall, TEXT("STREAM"), TEXT("BLUE") );
}
return WcaFinalize(er);
}
Secondly, I make two conditions per two features:
<Feature Id='Complete' Level='1'>
<Feature Id="Red" ConfigurableDirectory="TARGETDIR" Title="F1" Level="0">
<Condition Level="1">STREAM</Condition>
</Feature>
<Feature Id="Blue" ConfigurableDirectory="TARGETDIR" Title="F2" Level="0">
<Condition Level="1">NOT STREAM</Condition>
</Feature>
</Feature>
Note that I don't define property inside of the wxs file previously, as I would like to set it from the custom action.
My custom action is called before InstallInitialize and Execute is immediate.
From the installation log I have confirmation that the property is set.
However, my conditional installation does not work, as it seems like what is in the condition is always evaluated as false.
I tried evaluating conditions:
STREAM, STREAM=RED, STREAM="RED", < ![CDATA[STREAM=RED]]>
I am running out of ideas what to do and would appreciate help.
Too late to test all of this, but here goes with some information. I will check back tomorrow. Essentially I think the problem is your custom action sequencing. Try before Costing.
Some things to consider:
Custom action sequencing: you need to sequence your custom action right and it needs to be present in both silent and interactive installation modes.
Did you try to sequence the set property custom action before CostInitialize? You state you set it before InstallInitialize, but try it before CostInitialize instead (you might have tried).
And did you remember to insert this custom action in the InstallUISequence as well as the InstallExecuteSequence? You need to insert in both sequences in case the setup runs in silent mode. Before CostInitialize in both sequences I believe.
Feature Level: manipulating features via the feature level and INSTALLLEVEL is just one way to do feature control, you can also set features via the command line or using a custom action.
Setting a feature level to 0 should hide the feature from view in the setup's custom dialog.
Setting a feature level higher than the setup's INSTALLLEVEL will deselect the feature from installation.
And the other way around setting a feature level lower or equal to the setup's INSTALLLEVEL will select the feature for installation.
The conditional syntax allowed is quite flexible, and could provide the functionality you need outright - but I have never used them properly. Here is an example from the Installshield forum.
ADDLOCAL & REMOVE: you can manipulate the feature selection by changing the values of the ADDLOCAL and REMOVE properties from a custom action (technically also REINSTALL and ADVERTISE) - and these properties can be set via the command line as well.
Win32: you can also use the Win32 functions MsiGetFeatureState and MsiSetFeatureState - from a C++ custom action - to set feature selection.
Frankly it is a bit mad the whole thing. Also keep in mind that there are feature action states (what is going to happen to a feature) and feature installed states (what state it is in). The Win32 function documentation should explain.
Cross-linking for easy retrieval:
Unselected Feature Being Installed
I have done something similar, but we ended up controlling this at a component level(adding the condition to the <Component/> elements instead of the feature element using a transform during heat). But our condition utilizes CDATA while also using double quotes for the value, which you don't list in what you've tried. So first I'd try the following conditions in your features:
<Condition><![CDATA[STREAM="RED"]]></Condition>
<Condition><![CDATA[STREAM="BLUE"]]></Condition>
If that still does not work, I would try the following:
Add the STREAM property with a default value to your WiX. Then test it with that default value to see if having the default value set to begin with makes it work. That could mean you need to set the property sooner, possibly off a UI event. <Property Id="STREAM" Value="RED"/>
As a last resort, you could add the conditions to each component as I did, but we only did that for very specific reasons, hopefully you can get the conditional feature to work with the above suggestions!
I hope the above fixes your problem, or at least leads you to the answer!
Thank you for your replies. In the end, a combination of your suggestions helped me.
I want to state what did and what did not work:
Adding property to WiX with a default value was not necessary (as well with adding property of this property Secure='yes')
Calling custom action before CostInitialize did not solve the problem on its own, but I believe it was one of the factors that resolved an issue.
Conditional sintax was corrected by:
a) Putting condition inside of CDATA and adding quotes to the value of property as suggested: <Condition><![CDATA[STREAM="RED"]]></Condition>
b) Reversing condition levels so feature has condition level 1 and condition has level 0. This means that feature is always installed, unless the condition expression is false.
Concerning the correct order of the custom actions, the description of the custom action type 51 contains the decisive hint:
"To affect a property used in a condition on a component or feature, the custom action must come before the CostFinalize action in the action sequence."
Related
I am using APEX 20.2.0.00.20. apex.region(region_static_id).widget() method should return a jQuery object according to the documentation. I am trying to figure out how to know what the object's properties and methods are, especially when they are not mentioned in the documentation. I tried running apex.region(calendar_region_static_id).widget() to return the object and inspect what properties and methods it has but I got S.fn.init [div#CAL_calendar.fc.ui-widget.fc-ltr, prevObject: S.fn.init(1)] 0: div#CAL_calendar.fc.ui-widget.fc-ltr length: 1 prevObject: S.fn.init [document] __proto__: Object(0)
I did not get the object. I do not know what s.fn.init or the rest of the returned code is?!
I see code like apex.region("calendar_static_id").widget().fullCalendar("getView"), so I assumed I should have gotten the jQuery object which has the "fullCalendar" method and more when I ran apex.region(calendar_region_static_id).widget(), but I have not.
Is this not the right way to inspect a jQuery object's properties and methods?
APEX integrates the FullCalendar widget, but it doesn't duplicate its documentation. Have a look here for a list of the FullCalendar methods and options.
In general, most (interactive) APEX regions are implemented as jQuery UI widgets. That means you can use them like this:
$('selector').widgetName('methodName'); //invokes said method
$('selector').widgetName('methodName', 'param1'); //invokes said method with a parameter
$('selector').widgetName('option', 'optionName'); //gets a specific option
$('selector').widgetName('option', 'optionName', 'newVal'); //sets a specific option
What's more, you can inspect all available options by running:
$('selector').widgetName('option');
And even have a look at the internal object, see all methods, public and otherwise, via:
$('selector').widgetName('instance');
Moreover, via its region interface, APEX offers an even easier way to reach those methods and options, even without having to know a region's widgetName:
// this
$('widgetSelector', 'staticId').widgetName('methodName');
// is equivalent to
apex.region('staticId').widget().widgetName('methodName');
// is quivalent to
apex.region('staticId').call('methodName');
The last way is the shortest and doesn't require knowing the real widget's id or widget name.
All of this helps when dealing with regular APEX widgets, such as the Interactive Grid.
apex.region('emp').call('instance'); //inspects all methods
apex.region('emp').call('option'); //inspects all options
This however does not work on the FullCalendar region, for reasons that are beyond me. It should help you navigate all other APEX regions, but for the FullCalendar you'll have to resort to their API docs.
Hope this helps a bit.
So, what can I say? How do I go about adding a page programmatically in MURA CMS? Preferably version 6.1.
I am building a plugin that needs to create a couple of pages in the Site Manager. I want to add this routine in the 'install' method of the 'plugin/plugin.cfc' component.
I have been unable to find any pointers to this on line, which, perhaps, means this particular issue cannot be resolved. But, I live in hope.
Thanks people, in advance, for any help on this one.
When you add content to Mura, the key is that you'll need to know the parentid of where you wish the new content item(s) to live. Other than that, Mura offers an easy way perform CRUD operations on content items. You can read more about it at http://docs.getmura.com/v6/back-end/base-mura-objects-beans/loading-beans/ (based on the version you've specified). I'd also like to point out that the documentation has been greatly expanded in the latest version which can be found at http://docs.getmura.com/v7/mura-developers/mura-beans-objects/common-bean-objects/content-bean/. While the concepts and syntax still apply to older version, some newer methods for working with Mura content objects have been added since v6.1. I also highly recommend upgrading soon, as v7.1 is about to be released as well (as of Feb. 2018).
That said, the only two required fields/attributes are title and parentid. Here's the basic code for being able to do what you need:
// load the parent content item
parentBean = $.getBean('content').loadBy(title='Home');
// you may want to verify the `parentBean` actually exists before proceeding
if ( parentBean.getIsNew() ) {
Throw(message='parentBean does NOT exist!');
}
// v6.1 syntax
newBean = $.getBean('content')
.setValue('title', 'Some Title')
.setValue('parentid', parentBean.getContentID())
.save();
// v7.0+ syntax
newBean = $.getBean('content')
.set('title', 'Some Title')
.set('parentid', parentBean.get('contentid'))
.save();
// after saving, you can check for errors
if ( !StructIsEmpty(newBean.getErrors()) ) {
WriteDump(var=newBean.getErrors(), abort=true);
}
You could clearly set other attributes (as defined in the earlier links) as well, if desired.
Cheers!
I am currently working with sitecore items that are in a draft workflow state. The following happens:
Create an item that will go into workflow draft state
Publish the item/publish the parent item (with sub items selected) to the web db from master db, ignore publish warning prompt
The item will appear in the web database but with no versions
This causes our controls to render the item but with standard values because there are no versions. Of course we could add a check for item.Versions.Count > 0 but my question is why this is happening?
Surely an item in draft workflow state should never appear in the web database in any way?
The workflow being used is pretty standard and has no customisation. The states and commands are:
Draft
Submit
Awaiting approval
Reject
Approve
Validation action
Approved
Auto publish
Thanks in advance.
I believe you can use the following property on each of your site nodes in the config.
<site name="website" filterItems="true" ... />
If setting this to true doesn't resolve the issue then you can add the following pipeline step, in addition to the above setting, before the Sitecore.Pipelines.FilterItem.EnsureFilteredItem step.
public class HideEmptyItem
{
public void Process(FilterItemPipelineArgs args)
{
if ((Context.Site != null && Context.Site.DisplayMode == DisplayMode.Normal) && args.Item.Paths.Path.StartsWith("/sitecore/content/"))
{
try
{
Context.Site.DisableFiltering = true;
if (args.Item.Database.GetItem(args.Item.ID, Context.Language).Versions.Count == 0)
{
args.FilteredItem = null;
}
}
finally
{
Context.Site.DisableFiltering = false;
}
}
}
}
Also ensure the following is set accordingly, the default is false;
<setting name="Publishing.PublishEmptyItems" value="false" />
I actually encountered this issue but in a different way. If you use publishing restrictions you can end up with an item that has no versions on it but can still be published. There are many ways for an item to end up with no versions, not just a new item with a single version that hasn't been pushed through a workflow. The above fix was actually provided by Sitecore support and worked for me.
If this doesn't work for you then I would suggest adding in check when items are published to see if they have any versions, although I believe that's what the fix above is supposed to do.
EDIT: Changed property from "hideEmptyItems" to "filterItems" and added further explanation.
My personal opinion is that you should be checking for version count. If you plan on ever supporting a multilingual site, it is entirely possible to have a version in one language, but not another (not approved yet in Spanish, for example). You would want your controls to handle that scenario (or execute fallback).
It is entirely valid that the current user in the current language may have no valid versions come back for them. I would expect that the business logic of the web controls should handle those scenarios.
To explicitly answer your question, this happens because the item is not null but it has no versions. A version is a dimension of an item and it happens to be "empty" if you have no version. So your code gets a valid non null item, but has no field values since there is no version and thus your controls don't fill with data.
This will only happen for new items. Example: create an item and you have v1 which is is in draft. You "publish" the item. The item goes out to the web DB but the v1 dimension of it does not, so you're left with a non null item with no versions.
As Jay said, the solution is to check for this when you query items.
First, the default workflow should set to the item's template "__Standard Values".
Second, workflow with "admin" account doesn't work. Try another account.
I´m trying to perform some actions in the pipeline "httpRequestBegin" only when necessary.
My processor is executed after Sitecore resolves the user (processor type="Sitecore.Pipelines.HttpRequest.UserResolver, Sitecore.Kernel" ), as i´m resolving the user too if Sitecore is not able to resolve it first.
Later, i want to add some rendering in the pipeline "insertRenderings", only if actions in the previous pipeline were executed (If i resolved the user, show a message), so i´m trying to save some "flag" in the first step, to check in the second.
My question is, where can I store that flag? I´m trying to find some kind of "per request" cache...
So far, I've tried:
The session: Wrong, it's too early, session doesn't exists yet.
Items (HttpContext.Current.Items): It doesn't work either, my item is not there on the seconds step.
So far i'm using the application cache (HttpContext.Current.Cache) with some unique key, but I don´t like this solution.
Anybody body knows a better approach to share this "flag"?
You could add a flag to the request header and then check it's existence in the latter pipelines, e.g.
// in HttpRequest pipeline
HttpContext.Current.Request.Headers.Add("CustomUserResolve", "true");
// in InsertRenderings pipeline
var customUserResolve = HttpContext.Current.Request.Headers["CustomUserResolve"];
if (Sitecore.MainUtil.GetBool(customUserResolve, false))
{
// custom logic goes here
}
This feels a little dirty, I think adding to Request.QueryString or Request.Params would been nicer but those are readonly. However, if you only need this for a one time deal (i.e. only the first time it is resolved) then it will work since in the next request the Headers are back to default without your custom header added.
HttpContext.Current.Cache or HttpRuntime.Cache could be the fastest solution here. Though this approach would not preserve data when the AppPool gets recycled.
If you add only a few keys to the cache and then maintain them, this solution might work for you. If each request puts an entry into the cache, it may eventually overflow the memory used by worker process in a long run.
As alternative to this you may try to use Sitecore.Context.ClientData property. It uses ClientDataStore that employs a database (look for clientDataStore section in the web.config file) to store data. These entries can survive the AppPool recycle.
Though if you use them a lot, it may become a bottleneck under the load when you need to write to and/or read from the entries.
If you do know that there could be a lot of entries created for sharing purposes, I'd create a scheduled task to clean up the data store from obsolete entries.
I know this is a very old question, but I just want post solution I worked around
Below will hold data per http request basis.
HttpContext.Current.Items["ModuleInfo"] = "Custom Module Info"
we can store data to httpcontext in one sitecore pipeline and retrieve in another...
https://www.codeproject.com/Articles/146455/When-Can-We-Use-HttpContext-Current-Items-to-Store
I'm a little baffled; I don't seem to be able to change any of the properties associated with an existing AWS::AutoScaling::Trigger.
This seems like an awfully basic thing you would want to do once you've got things set up; perhaps adjusting the period, breachduration, increments, upper+lower threshold.
If I take my template, just change the "UpperThreshold" property, and try to update my existing stack with it, I get this error and the update fails:
The following resource(s) failed to update: [AutoscalingTrigger].
Updating the properties of AWS::AutoScaling::Trigger resources is not supported.
Suggestions?
Thanks...
I'm not sure if this helps or not, but I came across this note on the UpdateAutoScalingGroup page:
"To update an Auto Scaling group with a launch configuration that has the InstanceMonitoring flag set to False, you must first ensure that collection of group metrics is disabled. Otherwise, calls to UpdateAutoScalingGroup will fail. If you have previously enabled group metrics collection, you can disable collection of all group metrics by calling DisableMetricsCollection."