Rule-based node creation: commerce product + product display node set - drupal

I'm trying to bind a Commerce product type to my own custom type node (serving as a display node). The goal is to enter new data in as few places as possible. I'm therefore exploring a rule-based creation of one type upon creation of the other. Seems like both directions are working. Of the two though, I prefer automatic creation of a Commerce Product upon user creation of Custom Type node, which will then serve as a product display.
I was wondering if anyone has been through this choice and could recommend this. Also, is the commerce_product_display_manager module necessary?

Commerce Product Display Manager is not necessary, I've gotten this to work and I've never used that module.
I went for the route of automatically creating a Node after saving the Product.
Below is my Rules export for this:
{ "rules_create_product_display" : {
"LABEL" : "Create Product Display",
"PLUGIN" : "reaction rule",
"REQUIRES" : [ "rules", "entity" ],
"ON" : [ "commerce_product_insert" ],
"IF" : [
{ "data_is" : { "data" : [ "commerce-product:type" ], "value" : "**PRODUCT_TYPE**" } }
],
"DO" : [
{ "entity_create" : {
"USING" : {
"type" : "node",
"param_type" : "**NODE_TYPE**",
"param_title" : "[commerce-product:title]",
"param_author" : [ "commerce-product:creator" ]
},
"PROVIDE" : { "entity_created" : { "entity_created" : "Created entity" } }
}
},
{ "data_set" : {
"data" : [ "entity-created:**PRODUCT_REFERENCE**" ],
"value" : [ "commerce-product" ]
}
}
]
}
}
You'll need to substitute your own values for:
PRODUCT_TYPE (product type that has been created)
NODE_TYPE (node type being created)
PRODUCT_REFERENCE (field that will reference the created product)
Sorry I can't dedicate more time to a better answer now, let me know if you'd like me to elaborate on the process of creating the above using the GUI

The above example was useful but here is a more specific one:
{ "rules_create_product_display_on_product_creation" : {
"LABEL" : "Create Product Display on Product creation",
"PLUGIN" : "reaction rule",
"REQUIRES" : [ "rules", "entity" ],
"ON" : [ "commerce_product_insert" ],
"IF" : [
{ "entity_is_of_type" : { "entity" : [ "commerce-product" ], "type" : "commerce_product" } }
],
"DO" : [
{ "entity_create" : {
"USING" : {
"type" : "node",
"param_type" : "product_display",
"param_title" : "[commerce-product:title]",
"param_author" : [ "commerce-product:creator" ]
},
"PROVIDE" : { "entity_created" : { "entity_created" : "Created entity" } }
}
},
{ "data_set" : {
"data" : [ "entity-created:field-product:0" ],
"value" : [ "commerce-product" ]
}
}
]
}
}
The only problem I had was with the second action ("data_set")- it was important to select "entity-created:field-product:0", not the "entity-created:field-product" to make it work because we want to assign specific product and not a list of products.
This example is using the standard product display node type (product_display) but you can change it with the one you are using. Also have in mind that this is working only for one product type - for every product type a separated rule should be created. You may create also a rule for deleting the product display node when deleting the product.
This rule is useful only when you have connection one product-one product display. If you need to add more products per product display (colors, images with different prices) then you have to use Commerce Bulk Product Creation module.

Related

Force Artifactory to use numerical comparison when searching?

I am trying to find the latest (or earliest, depending on comparison operator) version of an RPM package (the RPM bit is important). I am using AQL query similar to this one:
items.find(
{ "$and" : [
{ "#rpm.metadata.name": { "$eq": "awesome_package"}},
{ "#rpm.metadata.version": { "$gte": "19.300.0.58"}} ]
})
.include("#rpm.metadata.version")
.sort( { "$asc": [ "name" ]})
As already answered by Artifactory KnowledgeBase, it's impossible to sort on properties, so instead of just sorting on #rpm.metadata.version and taking the first top result by using .limit(1) I must use property condition in the find clause.
It appears though that Artifactory's built-in comparison is purely lexicographic, so for the query above I get the following result:
{
"results" : [ {
"repo" : "yum-private-local",
"path" : "some/path",
"name" : "awesome_package-19.300.0.9-1.noarch.rpm",
"properties" : [ {
"key" : "rpm.metadata.version",
"value" : "19.300.0.9"
} ]
},{
"repo" : "yum-private-local",
"path" : "some/path",
"name" : "awesome_package-19.300.0.58-0.noarch.rpm",
"properties" : [ {
"key" : "rpm.metadata.version",
"value" : "19.300.0.58"
} ]
},{
"repo" : "yum-private-local",
"path" : "some/path",
"name" : "awesome_package-19.300.0.59-0.noarch.rpm",
"properties" : [ {
"key" : "rpm.metadata.version",
"value" : "19.300.0.59"
} ]
} ],
"range" : {
"start_pos" : 0,
"end_pos" : 3,
"total" : 3
}
}
This result includes version 19.300.0.9, which, according to RPM spec, is older than what I am searching for (>= 19.300.0.58) and shouldn't be included in the results, but Artifactory finds it nonetheless, most likely due to its search comparisons being lexicographic.
Also note the ordering of the results, which does appear to use numerical sorting (version "19.300.0.9" comes before "19.300.0.58" and "19.300.0.59").
Question: is it possible to force Artifactory to use numerical (SemVer) comparison in search criteria? If not, is there any other way I can exclude irrelevant versions from the result list?
Although not in lines with what is asked but instead of name sorting if done by the created field would also be helpful.
created:

Firebase: structuring data via per-user copies? Risk of data corruption?

Implementing an Android+Web(Angular)+Firebase app, which has a many-to-many relationship: User <-> Widget (Widgets can be shared to multiple users).
Considerations:
List all the Widgets that a User has.
A User can only see the Widgets which are shared to him/her.
Be able to see all Users to whom a given Widget is shared.
A single Widget can be owned/administered by multiple Users with equal rights (modify Widget and change to whom it is shared). Similar to how Google Drive does sharing to specific users.
One of the approaches to implement fetching (join-style), would be to go with this advice: https://www.firebase.com/docs/android/guide/structuring-data.html ("Joining Flattened Data") via multiple listeners.
However I have doubts about this approach, because I have discovered that data loading would be worryingly slow (at least on Android) - I asked about it in another question - Firebase Android: slow "join" using many listeners, seems to contradict documentation .
So, this question is about another approach: per-user copies of all Widgets that a user has. As used in the Firebase+Udacity tutorial "ShoppingList++" ( https://www.firebase.com/blog/2015-12-07-udacity-course-firebase-essentials.html ).
Their structure looks like this:
In particular this part - userLists:
"userLists" : {
"abc#gmail,com" : {
"-KBt0MDWbvXFwNvZJXTj" : {
"listName" : "Test List 1 Rename 2",
"owner" : "xyz#gmail,com",
"timestampCreated" : {
"timestamp" : 1456950573084
},
"timestampLastChanged" : {
"timestamp" : 1457044229747
},
"timestampLastChangedReverse" : {
"timestamp" : -1457044229747
}
}
},
"xyz#gmail,com" : {
"-KBt0MDWbvXFwNvZJXTj" : {
"listName" : "Test List 1 Rename 2",
"owner" : "xyz#gmail,com",
"timestampCreated" : {
"timestamp" : 1456950573084
},
"timestampLastChanged" : {
"timestamp" : 1457044229747
},
"timestampLastChangedReverse" : {
"timestamp" : -1457044229747
}
},
"-KByb0imU7hFzWTK4eoM" : {
"listName" : "List2",
"owner" : "xyz#gmail,com",
"timestampCreated" : {
"timestamp" : 1457044332539
},
"timestampLastChanged" : {
"timestamp" : 1457044332539
},
"timestampLastChangedReverse" : {
"timestamp" : -1457044332539
}
}
}
},
As you can see, the copies of shopping list "Test List 1 Rename 2" info appears in two places (for 2 users).
And here is the rest for completeness:
{
"ownerMappings" : {
"-KBt0MDWbvXFwNvZJXTj" : "xyz#gmail,com",
"-KByb0imU7hFzWTK4eoM" : "xyz#gmail,com"
},
"sharedWith" : {
"-KBt0MDWbvXFwNvZJXTj" : {
"abc#gmail,com" : {
"email" : "abc#gmail,com",
"hasLoggedInWithPassword" : false,
"name" : "Agenda TEST",
"timestampJoined" : {
"timestamp" : 1456950523145
}
}
}
},
"shoppingListItems" : {
"-KBt0MDWbvXFwNvZJXTj" : {
"-KBt0heZh-YDWIZNV7xs" : {
"bought" : false,
"itemName" : "item",
"owner" : "xyz#gmail,com"
}
}
},
"uidMappings" : {
"google:112894577549422030859" : "abc#gmail,com",
"google:117151367009479509658" : "xyz#gmail,com"
},
"userFriends" : {
"xyz#gmail,com" : {
"abc#gmail,com" : {
"email" : "abc#gmail,com",
"hasLoggedInWithPassword" : false,
"name" : "Agenda TEST",
"timestampJoined" : {
"timestamp" : 1456950523145
}
}
}
},
"users" : {
"abc#gmail,com" : {
"email" : "abc#gmail,com",
"hasLoggedInWithPassword" : false,
"name" : "Agenda TEST",
"timestampJoined" : {
"timestamp" : 1456950523145
}
},
"xyz#gmail,com" : {
"email" : "xyz#gmail,com",
"hasLoggedInWithPassword" : false,
"name" : "Karol Depka",
"timestampJoined" : {
"timestamp" : 1456952940258
}
}
}
}
However, before I jump into implementing a similar structure in my app, I would like to clarify a few doubts.
Here are my interrelated questions:
In their ShoppingList++ app, they only permit a single "owner" - assigned in the ownerMappings node. Thus no-one else can rename the shopping list. I would like to have multiple "owners"/admins, with equal rights. Would such a keep-copies-per-user structure still work for multiple owner/admin users, without risking data corruption/"desynchronization" or "pranks"?
Could data corruption arise in scenarios like this: User1 goes offline, renames Widget1 to Widget1Prim. While User1 is offline, User2 shares Widget1 to User3 (User3's copy would not yet be aware of the rename). User1 goes online and sends the info about the rename of Widget1 (only to his own and User2's copies, of which the client code was aware at the time of the rename - not updating User3's copy). Now, in a naive implementation, User3 would have the old name, while the others would have the new name. This would probably be rare, but still worrying a bit.
Could/should the data corruption scenario in point "2." be resolved via having some process (e.g. on AppEngine) listening to changes and ensuring proper propagation to all user copies?
And/or could/should the data corruption scenario in point "2." be resolved via implementing a redundant listening to both changes of sharing and renaming, and propagating the changes to per-user copies, to handle the special case? Most of the time this would not be necessary, so it could result in performance/bandwidth penalty and complicated code. Is it worth it?
Going forward, once we have multiple versions deployed "in the wild", wouldn't it become unwieldy to evolve the schema, given how much of the data-handling responsibility lies with the code in the clients? For example if we add a new relationship, that the older client versions don't yet know about, doesn't it seem fragile? Then, back to the server-side syncer-ensurerer process on e.g. AppEngine (described in question "3.") ?
Would it seem like a good idea, to also have a "master reference copy" of every Widget / shopping-list, so as to give good "source of truth" for any syncer-ensurerer type of operations that would update per-user copies?
Any special considerations/traps/blockers regarding rules.json / rules.bolt permissions for data structured in such a (redundant) way ?
PS: I know about atomic multi-path updates via updateChildren() - would definitely use them.
Any other hints/observations welcome. TIA.
I suggest having only one copy of a widget for the entire system. It would have an origin user ID, and a set of users that have access to it. The widget tree can hold user permissions and change history. Any time a change is made, a branch is added to the tree. Branches can then be "promoted" to the "master" kind of like GIT. This would guarantee data integrity because past versions are never changed or deleted. It would also simplify your fetches... I think :)
{
users:[
bob:{
widgets:[
xxx:{
widgetKey: xyz,
permissions: *,
lastEdit...
}
]
}
...
]
widgets:[
xyz:{
masterKey:abc,
data: {...},
owner: bob,
},
...
]
widgetHistory:[
xyz:[
v1:{
data:{...},
},
v2,
v3
]
123:[
...
],
...
]
}

How do you get a list of all developers and apps for an API in Apigee?

Say you have exposed an API Product in Apigee. You would like to get a list of all the developers and their apps that have registered for the API Product.
There is a call to return those, documented here:
http://apigee.com/docs/api/get-list-keys-apps-developers-or-companies-api-product
But the IDs it returns appear to be useless. If you try developers, for instance:
https://api.enterprise.apigee.com/v1/organizations/YOUR-ORG/apiproducts/YOUR-PRODUCT?query=list&entity=developers
you get back a list of IDs. But to find which developer a given ID relates to is impossible, as the call to get a developer:
http://apigee.com/docs/api/get-developer
only accepts an email address.
How can I get a list of all the developers and their apps registered for a given API product?
The Apps a developer has is nested in each developer:
https://api.enterprise.apigee.com/v1/o/{your org}/developers
will return a list of developers like this:
["email#domain.com", "email2#domain2.com"]
Then you have to loop through each developer to get a list of their apps:
https://api.enterprise.apigee.com/v1/o/{your org}/developers/tesla#weathersample.com
Which gives you a bunch of meta data including apps:
{
"apps" : [ "weather" ],
"companies" : [ ],
"email" : "tesla#weathersample.com",
"developerId" : "Hk5mmLw9kKIM95qF",
"firstName" : "Nikolai",
"lastName" : "Tesla",
"userName" : "Nikolai",
"organizationName" : "jokeindex",
"status" : "active",
"attributes" : [ ],
"createdAt" : 1357858239543,
"createdBy" : "noreply_admin#apigee.com",
"lastModifiedAt" : 1357858239543,
"lastModifiedBy" : "noreply_admin#apigee.com"
}
Finally, if you look at each app you can see the products associated with that developer app:
https://api.enterprise.apigee.com/v1/o/{your org}/developers/tesla#weathersample.com/apps/weather
Gets you this detail:
{
"accessType" : "read",
"appFamily" : "default",
"appId" : "030fdcea-cf97-40b1-96df-12084aea513c",
"attributes" : [ {
"name" : "Developer",
"value" : "tesla#weathersample.com"
}, {
"name" : "DisplayName",
"value" : "Weather"
}, {
"name" : "Notes",
"value" : "not yet"
}, {
"name" : "lastModifier",
"value" : ""
} ],
"callbackUrl" : "http://example.com/callback",
"createdAt" : 1363578857830,
"createdBy" : "adminui#apigee.com",
"credentials" : [ {
"apiProducts" : [ {
"apiproduct" : "weather",
"status" : "approved"
} ],
"attributes" : [ ],
"consumerKey" : "{key}",
"consumerSecret" : "{key}",
"expiresAt" : -1,
"scopes" : [ ],
"status" : "approved"
} ],
"developerId" : "Hk5mmLw9kKIM95qF",
"lastModifiedAt" : 1386042817268,
"lastModifiedBy" : "michael.bissell#apigee.com",
"name" : "weather",
"scopes" : [ ],
"status" : "approved"
}
Take a look at the Org Snapshot Tool on git if you want to interrogate the entire org with one script:
https://github.com/apigee/api-platform-samples/tree/master/tools
This will interrogate every developer and every app and put it into a nice tree structure in your file system for future reference.

Drupal 7 Rules - on cron, check date field and if past set field [Status] from “active” to “ended”

OK... let me start by saying I know there is a similar post here (How to create a Drupal rule to check (on cron) a date field and if passed set field "status" to "ended"?) but the answer on that post does not work. Step 4 (In the component add the condition 'Data comparison' and select node:type) does not work or even exists as an option.
What I need to do is this:
On Cron > If content type is event and the end date has passed the current date then change the status field from Active to Ended. (select list)
I was able to do this by using the Event: Content is viewed but I really need to to work when cron is ran.
Side note: with the current version I have (Content is viewed) it does change Active to Ended but it also for some reason deletes the title of the node which is strange becuase the title filed is required by Drupal... any idea wht that is happening?
Not sure if it helps but here is an export of what I have done myself:
{ "rules_event_status" : {
"LABEL" : "Event Status",
"PLUGIN" : "reaction rule",
"ACTIVE" : false,
"REQUIRES" : [ "rules", "php" ],
"ON" : [ "node_view" ],
"IF" : [
{ "node_is_of_type" : { "node" : [ "node" ], "type" : { "value" : { "event" : "event" } } } },
{ "AND" : [] },
{ "php_eval" : { "code" : "\/\/dpm(strtotime($node-\u003Efield_event_date_time[LANGUAGE_NONE][0][\u0027value2\u0027]));\r\nif (time() \u003E strtotime($node-\u003Efield_event_date_time[LANGUAGE_NONE][0][\u0027value2\u0027]))\r\n{\r\n return true;\r\n}" } }
],
"DO" : [
{ "data_set" : { "data" : [ "node:field-event-status" ], "value" : "Ended" } }
]
}
}
Any help is very much appreciated.
Thanks
C
to use any custom fields or fields created by other modules than node, you have to add condition "entity has field" to your rules which will make that field "visible" and accesible for later work
side note: I think you can do the date comparison without php_eval, just add another entity has field condition and create "data comparison" condition. There should be tokens available to your needs
Not sure I fully understand the question: rules can be triggered by cron.
You should be able to get it to run when cron executes by picking the "React on event" attribute of the rule to "System > Cron maintenance tasks are executed".
Am I missing something?

How to add new rules in drupal

I'm trying to add some rules programmatically, I'm following this tutorial to manage different price list depending of the rules. To create the rules it usesa default_rules_configuration hook which will be executed "when the rules will be loaded".
1 - It's not really clear, when "rules are being loaded", apparently running the cron do it. Is it the only way to trigger it ?
2 - Is there a way to add rules programmatically, so rule can be added in the insert role hook, or is this default_rules hook the only way to do it ?
Thanks
1 - According to hook_default_rules_configuration() documentation:
This hook is invoked when rules configurations are loaded.
The function is actually called when you clear your cache as this is when Drupal rebuilds the default entities provided in code through entity_defaults_rebuild().
You can examine the full call stack as to how hook_default_rules_configuration function is called using debug_backtrace()
2 - To set a rule that reacts on inserting a role, you actually have to create a rule that reacts on a user insert action and then check the role saved to see if it matches the role that you're interested in reacting to.
I find it easier to do this via the UI. Here's an export of a rule that checks to see if the user is assigned the anonymous role and sends an email to admin if so:
{ "rules_role_change_rule" : {
"LABEL" : "Role change rule",
"PLUGIN" : "reaction rule",
"REQUIRES" : [ "rules" ],
"ON" : [ "user_insert" ],
"IF" : [
{ "user_has_role" : { "account" : [ "account" ], "roles" : { "value" : { "1" : "1" } } } }
],
"DO" : [
{ "mail" : {
"to" : "admin#website.com",
"subject" : "User role changed",
"message" : "User role has changed",
"from" : "drupal#website.com",
"language" : [ "" ]
}
}
]
}
}
You would still have to implement hook_default_rules_configuration but replace the rule in the tutorial with one that suits your needs.

Resources