Include "Change Note" when creating content from InvokeFactory - plone

I am creating a content item from a PloneFormGen Form Custom Script Adapter using invokeFactory. Everything is working fine so far, however we want to start generating a comment to be included in the create action, for the history of the item. The comment itself will be generated using fields from the form and some preset text.
Is this something that would be possible from PFG?
The content type is a custom type, and it is versionable. Using Plone 4.3.2, PFG 1.7.14
EDIT
My current code:
from Products.CMFPlone.utils import normalizeString
portal_root = context.portal_url.getPortalObject()
target = portal_root['first-folder']['my-folder']
form = request.form
title = "My Title: "+form['title-1']
id = normalizeString(title)
id = id+"_"+str(DateTime().millis())
target.invokeFactory(
"MyCustomType",
id=id,
title=title,
text=form['comments'],
relatedItems=form['uid']
)
I have tried using keys like comments, comment, message, and even cmfeditions_version_comment within the target.invokeFactory arguments. No luck so far.

I'm not sure if that's possible in a custom script adapter.
The action of you first entry is None. The history automatically shows Create if the action is None. This is implemented here (plone.app.layout.viewlets.content)
# On a default Plone site you got the following
>>> item.workflow_history
{'simple_publication_workflow': ({'action': None, 'review_state': 'private', 'actor': 'admin', 'comments': '', 'time': DateTime('2014/10/02 08:08:53.659345 GMT+2')},)}
Key of the the dict is the workflow id and the value is a tuple of all entries.
So you can manipulate the entry like you want. But I don't know if this is possible with restricted python (custom script adapter can only use restricted python).
But you could also add a new entry, by extending you script with:
...
new_object = target.get(id)
workflow_tool = getToolByName(new_object, 'portal_workflow')
workflows = workflow_tool.getWorkflowsFor(new_object)
if not workflows:
return
workflow_id = workflows[0].id # Grap first workflow, if you have more, take the the one you need
review_state = workflow_tool.getInfoFor(new_object, 'review_state', None)
history_entry = {
'action' : action, # Your action
'review_state' : review_state,
'comments' : comment, # Your comment
'actor' : actor, # Probably you could get the logged in user
'time' : time,
}
workflow_tool.setStatusOf(workflow_id, context, history_entry)

Related

How to update djangocms-blog post extensions fields position?

I have some post extensions adding new fields in my Posts objects.
I created the migration, launched my website, and I thought that maybe customizing the fieldsets would allow me to customize the position of the post extensions fieldset too.
That didn't seems to be the case. I created a new SmallIntegerField named my_new_field in a PostExtension class that I registered using blog_admin.register_extension(PostExtensionInline) (I also created the PostExtensionInline class by following the doc).
I added a breakpoint in my update_fields function that I'm using to update the order of the fields of my posts (see this SO question and its answer for more infos on this), and I can't find any mention to my_new_field in the fgets arg:
Quit the server with CONTROL-C.
> /home/me/my-test-app/my_test_app/update_fields.py(3)update_fields()
-> return fsets
(Pdb) l
1 def update_fields(fsets, request, obj):
2 breakpoint()
3 -> return fsets
[EOF]
(Pdb) fsets
[(None, {'fields': ['title', 'subtitle', 'slug', 'publish', 'categories', 'abstract', 'sites', 'author']}), ('Info', {'fields': [['tags', 'related'], ['date_published', 'date_published_end', 'date_featured'], 'app_config', 'enable_comments'], 'classes': ('collapse',)}), ('Images', {'fields': [['main_image', 'main_image_thumbnail', 'main_image_full']], 'classes': ('collapse',)}), ('SEO', {'fields': [['meta_description', 'meta_title', 'meta_keywords']], 'classes': ('collapse',)})]
How can I update my field position? (see edit below)
edit: I can't think of a way to tweak the order of the post extension fields. But I realized that my real problem (yeah yeah that's a case of XYproblem) is that I want conditional inline (only include the post extension for a certain apphook that's using a defined BlogConfig instance.
How to conditionally add the inline post extension form/fields to my admin create form based on the BlogConfig instance?
So I figured it out (and it's not pretty, but it works).
I added those lines in my admin.py:
# replace PostAdmin get_inlines function in order to hide event_date on regular blog posts
from djangocms_blog.admin import PostAdmin
from .misc import get_inline_instances as patched_get_inline_instances
PostAdmin.get_inline_instances = patched_get_inline_instances
And here's my code on misc.py:
def get_inline_instances(self, request, obj=None):
from djangocms_blog.cms_appconfig import BlogConfig
from djangocms_blog.admin import PostAdmin
from json import loads
inline_instances = super(PostAdmin, self).get_inline_instances(request, obj)
if "app_config" in request.GET:
# get blog config instance from request
blog_config = BlogConfig.objects.filter(pk=request.GET["app_config"])
# get config from saved json
config = loads(blog_config.values()[0]["app_data"])
# get template_prefix from config
if config:
template_prefix = config['config']['template_prefix']
if template_prefix == "djangocms_blog_agenda":
return inline_instances
return []
I used the template_prefix value in a BlogConfig instance of djangocms_blog to update the code based on the request:
If we have "djangocms_blog_agenda" in the template_prefix, then return the inline instances.
If we don't have this (default BlogConfig, another BlogConfig that do not need my current post extension fields), return an empty list.
The result can be viewed here:
Blog posts list screenshot, we can see that the articles are displayed by most recent "published date" first.
We can select our blog config after a click on "Blog > Add Post...".
Agenda post creation screenshot, we can see that there's a post extension named "event date" (not present in Blog post creation screenshot).
Agenda list screenshot, we can see that the "events" are displayed by nearest future "event date" date.

Update an object with notation with a parameter?

In my firebase i have a collection, inside there is a document, and inside there is an object :
object 1
key1:value
key2:value
key3:value
I would like to only update certain keys inside an object say
object1 - key1 and key2.
to do that, i need notation.
the problem is that I pass a parameter to the function that save :
function updateit(product,target)
{
db.collection("Stores").doc(target).update({
product
})
So here if I pass a product that contains only key 1, it will override the previous.
So, I tried to pass this object with notation :
product["product"+".title"] = "xxxxx"; // a new pair in product to pass
and it didn't work, it will save a new object (override) with fields like :
product
product.title=xxxxx
How should you do such a simple thing ?
ok obviously, this is the answer :
db.collection("Stores").doc(targetStore).update(
product // no {} around 'product', not as object!
)
see the comment that explains it all.

Pass additional parameters to django_table2 TemplateColumn

In my django project I have a lot of tables which return models. The last column is mostly an Action-Column where users may edit or delete an instance. How do I proceed if I want to pass additional arguments to TemplateColumn if in some tables I want an edit and delete button and in other tables I only need an edit and info button? I want to use the same template.html but with conditions in it. Here what I have in Table:
import django_tables2 as tables
from select_tool.models import DefactoCapability
class DefactoCapabilityTable(tables.Table):
my_column = tables.TemplateColumn(verbose_name='Actions', template_name='core/actionColumnTable.html')
class Meta:
model = DefactoCapability
template_name = 'django_tables2/bootstrap-responsive.html'
attrs = {'class': 'table table-xss table-hover'}
exclude = ( 'body', )
orderable = False
And how do I check perms on the actions in order to display the button or not?
Quoting the TemplateColumn docs
A Template object is created [...] and rendered with a context containing:
record – data record for the current row
value – value from record that corresponds to the current column
default – appropriate default value to use as fallback
row_counter – The number of the row this cell is being rendered in.
any context variables passed using the extra_context argument to TemplateColumn.
So you could do something like this:
my_column = tables.TemplateColumn(
template_name='core/actionColumnTable.html',
extra_context={
'edit_button': True,
}
)
The context also contains the complete context of the template from where {% render_table %} is called. So if you have 'django.template.context_processors.request' in your context_processors, you can access the current user using {{ request.user }}.

Web2py: Sending JSON Data via a Rest API post call in Web2py scheduler

I have a form whose one field is type IS_JSON
db.define_table('vmPowerOpsTable',
Field('launchId',label=T('Launch ID'),default =datetime.datetime.now().strftime("%d%m%y%H%M%S")),
Field('launchDate',label=T('Launched On'),default=datetime.datetime.now()),
Field('launchBy',label=T('Launched By'),default = auth.user.email if auth.user else "Anonymous"),
Field('inputJson','text',label=T('Input JSON*'),
requires = [IS_NOT_EMPTY(error_message='Input JSON is required'),IS_JSON(error_message='Invalid JSON')]),
migrate=True)
When the user submits this Form, this data is also simultaneously inserted to another table.
db.opStatus.insert(launchId=vmops_launchid,launchDate=vmops_launchdate
,launchBy=vmops_launchBy,opsType=operation_type,
opsData=vmops_inputJson,
statusDetail="Pending")
db.commit()
Now from the scheduler, I am trying to retrieve this data and make a POST request.
vm_power_opStatus_row_data = vm_power_opStatus_row.opsData
Note in the above step I am able to retrieve the data. (I inserted it in a DB and saw the field exactly matches what the user has entered.
Then from the scheduler, I do a POST.
power_response = requests.post(vm_power_op_url, json=vm_power_opStatus_row_data)
The POST request is handled by a function in my controller.
Controller Function:
#request.restful()
def vmPowerOperation():
response.view = 'generic.json'
si = None
def POST(*args, **vars):
jsonBody = request.vars
print "Debug 1"+ str(jsonBody) ##-> Here it returns blank in jsonBody.
But if I do the same request from Outside(POSTMAN client or even python request ) I get the desired result.
Is anything going wrong with the data type when I am trying to fetch it from the table.
power_response = requests.post(vm_power_op_url,
json=vm_power_opStatus_row_data)
It appears that vm_power_opStatus_row_data is already a JSON-encoded string. However, the json argument to requests.post() should be a Python object, not a string (requests will automatically encode the Python object to JSON and set the content type appropriately). So, the above should be:
power_response = requests.post(vm_power_op_url,
json=json.loads(vm_power_opStatus_row_data))
Alternatively, you can use the data argument and set the content type to JSON:
power_response = requests.post(vm_power_op_url,
data=vm_power_opStatus_row_data,
headers={'Content-Type': 'application/json')
Also, note that in your REST POST function, request.vars is already passed to the function as **vars, so within the function, you can simply refer to vars rather than request.vars.

Why use an object when denormalising data?

In the recent blog post on denormalising data, it suggests logging all of a user's comments beneath each user like so:
comments: {
comment1: true,
comment2: true
}
Why is this not a list like so:
comments: [
"comment1",
"comment2",
]
What are the advantages? Is there any difference at all? While I'm at it, how would you go about generating unique references for these comments for a distributed app? I was imagining that with a list I'd just push them onto the end and let the array take care of the index.
Firebase only ever stores objects. The JS client converts arrays into objects using the index as a key. So, for instance if you store the following array using set:
comments: [
"comment1",
"comment2"
]
In Forge (the graphical debugger), it will show up as:
comments:
0: comment1
1: comment2
Given this, storing the ID of the comment directly as a key has the advantage that you can refer to it directly in the security rules, for example, with an expression like:
root.child('comments').hasChild($comment)
In order to generate unique references for these comments, please use push (https://www.firebase.com/docs/managing-lists.html):
var commentsRef = new Firebase("https://<example>.firebaseio.com/comments");
var id = commentsRef.push({content: "Hello world!", author: "Alice"});
console.log(id); // Unique identifier for the comment just added.

Resources