I have setup a content rule that gets triggered when I change the state of a dexterity object through the web interface, but when I change the state programmatically the content rule is not triggered.
I use the typical workflow_tool.doActionFor for changing the review_state. The review_state in the catalog/object appears changed to the new state.
Any clues??
I'm using plone 4.2.5 and dexterity 1.2.1.
This is the code I use to change the state:
cart = brains[0].getObject()
wftool = getToolByName(self.context, 'portal_workflow')
wftool.doActionFor(cart, 'charge')
wftool.doActionFor(cart, 'pay')
modified(cart)
Finally I found out that triggering only one transition at a time, plone fires the content rule as expected.
Therefore in my case I setup a workaround in the following way:
The states are: created -> charged -> paid, and the content rule fires when changing to state paid. Sometimes I must change the state from created to paid in one transaction. I enabled the pay transition from state created bypassing the intermediate state charged. So, I only need to trigger one transition to pay a created object, and the content rule works.
Related
I created a click event in adobe launch which will capture the value of the link and send it to analytics. I have created a data element for saving the value and in my DOM I am saving value in local storage.
Local storage code:
$('.card').click(function() {
var name = $(this).text();
localStorage.setItem('productName', name);
});
My problem is when I click the first link no value got saved, but after when I click second link it saves the value of first link and on third link saves value of second link and so on. I want to save current link value in evar3 variable.
Data element:
Rule:
Set variables:
Thanks,
Harshit
I'm scratching my head a little bit about why your jQuery selector doesn't match your Rule selector, but that's probably not immediately related or relevant, considering you said you are seeing data pop, in general, so at face value, I'm going to ignore that.
But in general, it sounds like your jQuery code is getting executed after the Rule is evaluated, so it's effectively one step behind. I'm not sure there's much if anything you can do about that if you aim to keep two separate click event listeners like this.
You're going to have to restructure to have one chain off the other (which I don't think is actually feasible with jQuery > Launch as-is. Maybe if you write two separate custom code blocks with promise chaining but that kind of sidesteps Launch and IMO is over-complicating things to begin with (see below)). Better yet, merge them into a single click event listener. On that note..
Why do you have two separate click event listeners? Is the sole reason for this to pass off the link text? Because you can reference the clicked element in the Launch Rule itself.
You should be able to reference %this.innerText% in the Set Variables fields. Or you can reference this object in custom code boxes within the Rule.
IOW at face value I don't see why you need or should be creating/using that jQuery listener or pushing to local storage like that.
Update:
You commented the following:
I tried with %this.innerText% it is also not showing current values. I
am pushing the value first to local storage because my link values are
generating on runtime through an API. They are not hardcoded. I am
also trying to figure out why my rule is getting fired before my
jquery is evaluated. But when I check in console using
_satellite.getVar('Product Name'); it shows me the correct value, but in debugger console value is wrong. Can you show me the way you want
to create rule to getting it fired correctly ? Thanks,
Okay so your link values are generated runtime through an API call? Well okay now that sounds like where the (timing) issue is, and it's the same issue in principle I mentioned you prolly had between the jQuery and Launch code. Assuming you can't change this dynamic link functionality, you have two options:
1. Explicitly trigger a launch rule (direct call rule) in a callback from the API code that's generating the link values.
This is the better method, because you won't have race condition issues with your API vs. link tracking code. The biggest caveat about this method though is that it requires requires you to actively add code to the site, which may or may not be feasible for you.
I have no idea what your code for generating the link looks like, but presumably it's some ajax call and generated from the success callback. So in the callback, you'd add a line of code something like this:
_satellite.track('product_image_click', {
text : elem.innerText
});
Again, I don't know what your API code that generates the link looks like, but presumably you have within it some element object you append to or update the DOM, so for this example, I will refer to that as elem.
'product_image_click' - This is the value you use for the direct call rule identifier in the interface, e.g. :
And then _satellite.track() call also includes an object payload in 2nd argument that you can pass to the direct call rule. So in the code above, I set a property named text and give it a value of elem.innerText.
Then, within the direct call rule, where you set variables, the data you passed can be referenced from event.details object within custom code box (e.g. event.details.text), or using % syntax within the interface fields (e.g. %event.details.text%).
2. Make use of setTimeout to delay evaluating the link click.
The one advantage of this method over option #1 is that it is passive. You don't have to add code directly to your site to make it work.
However, this is the shadier option, because you don't really know how long it will take for your link to be generated. But generally speaking, if you can determine it takes on average say 250ms for the link generation API to do its thing, and you set the timeout to be called at say 300-500ms, then you will probably be okay most of the time. However, it's never a 100% guarantee.
In addition, if clicking on the link ultimately redirects the visitor to another page, then this solution will likely not work for you at all, since the browser will almost certainly have navigated before this has a chance to execute. Because of this, I normally I wouldn't even mention this as an option, but since you mentioned this link involves an API that generates the link values when clicked, I figured maybe this isn't a navigation / redirect link, so maybe this is an option you can go for, if you can't do option #1.
First, create a direct call rule the same as shown in option #1. This will be the rule that receives the link text and makes the Adobe Analytics (or whatever other marketing tag) calls.
Then, create a separate rule as a click event, similar to what you are currently trying to do (listening for the link click, based on your css selector). In this rule, you won't be setting any AA variables. Instead, add a custom js box with code like this:
var elem = this;
(function (elem) {
window.setTimeout(function() {
_satellite.track('product_image_click', {
text : elem.innerText
});
}, 500);
})(elem);
So when the rule is triggered, it will create a setTimeout callback to call the first rule, passing the link text in the payload. And.. hopefully the 500ms timeout shown in the code example is enough time for the API to do its thing. You may be able to adjust it up or down.
Rather than defining it in Data Element I would say its better to keep it directly in the Rule itself. Please try with this %this.#text%.
Let me know if this helped.
I have a folderish dexterity content type and I have an event subscriber. When the content type is created, I create a Collection, which shows the children in the container according to several parameters. After the collection is created, I try to set the default page of the container to the collection.
def myContainerAdded(my_container, event):
#get container
#set advanced query for collection
#create collection with api.create
my_container.setDefaultPage(new_collection.id)
The subscriber in configure.zcml
<subscriber
for="my.product.my_container.IMyContainer
zope.lifecycleevent.interfaces.IObjectAddedEvent"
handler=".events.myContainerAdded" />
Unfortunately, the default page is not being fully set. It just shows the container page, but the Collection is selected under the 'Display' drop down.
If I click "Change content item as default view" and select the collection, it does change the default page to the collection.
Earlier, I was using a "setuphandler" to setup a folder structure (as opposed to an add event), and setDefaultPage was working. Am I forgetting a step since I'm attempting this through an event?
I am using plone.4.3.
Edit: I also tried:
my_container.default_page = new_collection.id
Edit:
I found something interesting. I temporarily commented out the code related to the collection in the event subscriber. I manually added the collection to the container object and then set the default page of the container to the collection. The container's default page was the collection.
Maybe something isn't getting indexed right?
In fact everything went well, it's just that after creating a Dexterity-based content-type, one will land on the default-view's URL, meaning '/view' is appended to the item's URL, which is an alias to the default-view-method and here resolves to the 'folder_listing'-template.
To overcome this quickly, you can add a redirect to the object's URL in the subscriber's method, without any view-name appended to the URL:
event.REQUEST.RESPONSE.redirect(my_container.absolute_url())
I am wondering if all asynchronous events and callbacks can be traced back to a specific source component by wrapping all component logic (including logic of non-component directives inside component) in a zone, then only that source component needs to be checked for changes along with any child components with changed inputs from the source component, assuming all changes are following the uni-directional flow.
Is this understanding sound?
Is this change detection strategy available in Angular2?
Why is Angular2 detecting changes on all the components after any asynchronous (XHR) event?
One whole Angular application runs in a single zone. Angular uses the zone to patch async APIs and uses notifications from these patched APIs to run change detection every time some async event happened.
The uni-directional flow is for [prop]="value" bindings that works only from parent to child.
Angular runs change detection from root to leafs.
If ChangeDetectionStrategy.OnPush is configured for a component, change detection skips these components (and their descendants) until some binding (inputs) have changed.
There are other strategies to optimize CD.
For example observables and promises, that actively notify about changes and don't need change detection.
Immutable objects which are guaranteed to not change their propery values.
Update
Angular doesn't know what values an event handler has changed. Properties of a component, of a global service, of object references that were passed around, .... It just assumes that when an event handler was called that probably something has changed and then runs a complete change detection cycle to propagate all bindings from parent to child.
Child do parent bindings are events anyway and therefore aren't updated during change detection.
... by wrapping all component logic (including logic of non-component directives inside component) in a zone, then only that source component needs to be checked for changes along with any child components with changed inputs from the source component, assuming all changes are following the uni-directional flow. Is this understanding sound?
When an template-bound event fires – e.g., (click)="doSomething()" – the doSomething() method is free to alter any component or application data. Template statements, such as our doSomething() event handler, are not bound by the unidirectional flow rule, as per the Angular docs:
Responding to events is the other side of Angular's "unidirectional data flow". We're free to change anything, anywhere, during this turn of the event loop.
This is why, by default, Angular's change detection has to check every template binding in every component after an event fires. (Well, after an event within the Angular zone fires.) Angular doesn't know what might have changed... it has to discover what has changed.
The unidirectional flow rule applies to template expressions such as {{some expression}} or [childInputProperty]="parent expression" or if you implement an input property setter method: #Input() set childInputProperty(variableName:type) { ... }.
Is this change detection strategy available in Angular2?
No, because it would severely limit what an event handler could do. Dirty-checking every template binding may not be the most efficient way to detect changes, but it makes it much easier for us to write our event handlers (i.e., to write our applications).
Why is Angular2 detecting changes on all the components after any asynchronous (XHR) event?
Angular doesn't want to limit what we can do in our event handlers. An event handler bound in ComponentA's template can change data that is local to ComponentA, but it can also change data that is in a service (and hence it can change data that is visible to other components), and it can change data in other components, e.g., by calling public APIs/methods on other components.
On my site, users can recieve notifications on different events (such as a new comments on a user's post). Users can view these notifications on a special page in their profile. I want when a user come to that page that he can distinguish new ("unread") notifications. Namely, I want the following behavior:
When a user comes to a notifications page, any notification that has never before been shown on that page, is highlighted.
If a user leaves that page and comes later, only the new notifications (that has appeared in the meantime) are highlighted.
Each notification is highlighted for as long as the user stays on the page.
If a new notification appears while the user is on page, it is immediately loaded and shown and highlighted.
If a notification changes and/or is removed while the user stays on the page, the page reflects this change (preferably keeping the highlight status).
I start with having a Notifications collection that has a read field showing whether this notification is "read" (i.e. has already been shown). Now I can highlight only notifications that has read set to false. However, it will not get the required behavior in a simple way.
The main problem is point 3, because I must somehow mark that the notification has been read, but have this mark be taken into account only on the next visit to page.
For example, I might try to set notification's read field to true on template render (for example, in Template.notification.rendered), but it will immediately force notification template update and the highlighting will be removed.
So the particular problem is how to keep the initial value of read property and not redraw the template on this property change?
(I thought also of different approach such as relying on some user-side event such as user leaving the page, and updating read only then, but this does not seem to be reliable.)
For now, I have implemented the following solution:
//// html
<template name='notification'>
<li class="list-group-item {{notificationClass}}">
...
</li>
</template>
//// coffee
Template.notification.helpers
notificationClass: ->
if (this.read && UI._templateInstance().wasRead)
undefined # no highlight
else
"list-group-item-info" # highlight
Template.notification.rendered = ->
this.wasRead = this.data.read
this.data.markAsRead() # ultimately updates the collection
that is, on rendered event I set the template property to remember whether this notification was read initially. This template property does not change as long as the template exists (and even if notification data changes, the template property is there). In template helper, instead of directly accessing the notification read property, I check template property.
This seems to work, but looks too complicated for me. So:
Are there any major disadvantages in this approach? For example, can template's rendered code be re-executed while a user stays on the page?
Is there some better approach for my problem?
How do I tell Meteor to stop publishing changes on a collection (for a moment)?
Also how to tell it to resume and that the collection changed?
Basically (on the server):
People = new Meteor.Collection("people")
insertPeople = ->
// Don't notify clients of the following changes
// Insert a bunch of people into the People collection
// Resume notifications
Put a flag in each document, 'updating'.
Add the new ones with this set to true; render their template with a css class that hides them based on this field.
When ready, update the collection to updating: false. They will be visible pretty quickly.
This being said, there are events you can plug into to make transitions more pleasant/animated. Didn't think you were asking that, but it may be a better answer.
To the comment:
Inserting a template for an additional document triggers DOM changes, which are fairly expensive, and then the device has to figure out how to display. Updating a property requires just the second part, that the device has to figure out how to display.