unit testing buttonAndHandler with a z3c.form - plone

I am simply looking to write a unit test that tests the methods in my dexterity SchemaForm that use the buttonAndHandler, but I wasn't able to find anything appropriate in either the z3c.form documentation nor the Dexterity Developer Manual. I believe I'm getting tripped up on the decorator behavior but I don't know how I should be programmatically calling these methods.
form = self.rf.restrictedTraverse('add-file')
#mform = getMultiAdapter((self.rf,self.request), name='add-file')
Using restrictedTraverse or getMultiAdapter yields the same object. So if I want to call form.addFileSendToEditors I pass the form as the first variable and what for "action"?

Basically you could get the handlers from the form and call the manually.
This is an example with a regular z3c.form and a Dexterity add form.
>>> form_view = self.rf.restrictedTraverse('add-file')
# If your form is a Dexterity DefaultAddForm view.
>>> form_view
<plone.dexterity.browser.add.DefaultAddView object at 0x10cbf0950>
# Get the form from the instance
>>> form_view.form
<class 'plone.dexterity.browser.add.DefaultAddForm'>
# Than you can get all handlers
>>> form_view.form.handlers
<Handlers [<Handler for <Button 'save' u'Save'>>, <Handler for <Button 'cancel' u'Cancel'>>]
# and all buttons
form_view.form.buttons.items()
[('save', <Button 'save' u'Save'>), ('cancel', <Button 'cancel' u'Cancel'>)]
# In _handlers you can see the buttons, with the corresponding handlers
form_view.form.handlers._handlers
((<Button 'save' u'Save'>, <Handler for <Button 'save' u'Save'>>), (<Button 'cancel' u'Cancel'>, Handler for <Button 'cancel' u'Cancel'>>))
# You can also get the handler by button
>>> save_button = form_view.form.buttons.items()[0]
>>> save_handler = form_view.form.handlers.getHandler(save_button)
<Handler for <Button 'save' u'Save'>>
# Once you have your handler, you can call it directly
save_handler.func(form_view.form_instance, save_button)
It depends on what you are doing if you have to setup a little bit more, to make your test work.
You did not give us enough informations about what you are doing in you handler.
This is taken from the z3c.form documentation:
I did not run this code for myself.
# You can test your actions also this, probably more readable :-)
from z3c.form.testing import TestRequest
from z3c.form import button
>>> request = TestRequest(form={'form.buttons.save': 'Save'})
>>> actions = button.ButtonActions(form_view.form_instance, request, None)
>>> actions.update()
>>> actions.execute()
# This executes your Save actions.

Related

Telegram Bot: job_queue.run_repeating cannot properly callback

I have been wrestling with this for a while. I can't seem to get the job_queue.run_repeating to call back a function properly.
My goal is for the user to issue command in the form of /alert to begin running the send_alert function on a fixed interval. I am not sure where I am going wrong despite having looked at at several examples. I can get regular commands to execute just fine, but not JobQueue callbacks.
Currently my code below throws an error on the start_alerts callback, saying NameError: name 'job_queue' is not defined. When I add job_queue accordingly to the function call, such as start_alerts(update, context, job_queue) it no longer updates the "alerts started!" message to my Telegram chat, but it also doesn't throw an error.
from telegram.ext import Updater, CommandHandler, JobQueue
def start_alerts(update, context):
context.bot.send_message(chat_id=update.effective_chat.id, text="alerts started!")
job_queue.run_repeating(send_alert, interval=5.0, first=1.0)
def send_alert(update, context):
message="this is an alert to send"
context.bot.send_message(chat_id=update.effective_chat.id, text=message)
updater = Updater(token=token, use_context=True)
dispatcher = updater.dispatcher
job_queue = updater.job_queue
job_queue.set_dispatcher(dispatcher)
dispatcher.add_handler(CommandHandler('alert', start_alerts))
updater.start_polling()
job_queue.start()
Within the start_alerts function, the variable job_queue is not defined. Use context.job_queue instead as explained in the JobQueue tutorial, the timerbot.py example and the documentation of CallbackContext.

Plone: custom variable in content rule

I have defined a dexterity content type myaddon.subscriber (subscriber = participant at an event).
I added a Content rule to send an email when a subscriber is approved (workflow state change to this one).
Each subscriber has an email address (email - field).
In Edit Mail Action form I see I can use variables like ${user_email} in Email recipients (Required)
The email where you want to send this message. To send it to different email addresses, just separate them with ,
This is working. In my case user_email is the email of logged in user - the person who approve the participant. The messages are sent when the state is changed. Perfect.
I need to define a variable ${subscriber_email} that will have the myaddon.subscriber.email value. How can I do this? I'm trying to find an example. So, how to use an field (email) of current changed object (subscriber) as variable in this Mail Action?
You can use IContextWrapper from plone.stringinterp 1.0.14+:
>>> new_context = IContextWrapper(context)(
... email=u"subscriber#example.com"
... )
>>> class SubscriberEmailSubstitution(BaseSubstitution):
... def safe_call(self):
... return self.wrapper.email
>>> sm = getGlobalSiteManager()
>>> sm.registerAdapter(SubscriberEmailSubstitution, (Interface, ), IStringSubstitution, name=u"subscriber_email")
>>> getAdapter(new_context, IStringSubstitution, 'subscriber_email')()
u'subscriber#example.com'
Imports:
>>> from zope.interface import Interface
>>> from plone.stringinterp.interfaces import IStringSubstitution
>>> from plone.stringinterp.interfaces import IStringSubstitutionInfo
>>> from zope.component import getGlobalSiteManager, getAdapter
>>> from plone.stringinterp.adapters import BaseSubstitution
>>> from plone.stringinterp.interfaces import IContextWrapper
See more stringinterp doctests.
Also see a fully working example within eea.pdf add-on.

plone.registry - How do I capture a change in registry when IRecordModifiedEvent is fired?

I'm trying to replicate an example of detecting the change in registry when IRecordModified is supposed to be fired.
https://pypi.python.org/pypi/plone.app.registry#registry-events
I try to adapt this to what I am doing, but a print statement I placed isn't firing.
In my registry.xml
<registry>
<records interface="my.product.utils.db_settings.IDBSettings">
</registry>
In my events.py
from my.product.utils.db_settings import IDBSettings
#adapter(IDBSettings, IRecordModifiedEvent)
def detectDBSettingsChange(settings, event):
print "detectDBSettingsChange"
In the module db_settings.py, which contain the control panel , I have:
from plone.app.registry.browser import controlpanel
class IDBSettings(Interface):
db_string = schema.TextLine(title=u"Database String",
description=u"String for database connection",
default=u"Some value"
)
class DBSettingsEditForm(controlpanel.RegistryEditForm):
schema = IDBSettings
label = u"Database settings"
description = u"String setup"
def updateFields(self):
super(DBSettingsEditForm, self).updateFields()
def updateWidgets(self):
super(DBSettingsEditForm, self).updateWidgets()
class DBSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
form = DBSettingsEditForm
In my configure.zcml in utils:
<include package="plone.app.registry" />
<browser:page
name="database-settings"
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
class=".db_settings.DBSettingsControlPanel"
permission="cmf.ManagePortal"
/>
When I go into the control panel, change the value, and then save, detectDBSettingsChange doesn't seem to work as the print statement is ignored. Am I working with the wrong event to capture the change in my registry?
I think you need a subscriber like described in the docs. I'm not sure that the adapter in your events.py is sufficient. Perhaps you can use provideHandler Method like described in plone.registry
You need to add a subscriber and register it like this in your configure.zcml file:
<configure xmlns="http://namespaces.zope.org/zope">
<subscriber
for="plone.registry.interfaces.IRecordModifiedEvent"
handler="your.package.your_subscriber"
/>
</configure>
Check the collective.fingerpointing package for a working example of this.

PUT requests with DjangoRestFramework

I am building a rest API and am wondering both about HTTP best practices I guess, and how that would apply to the DRF. When sending a PUT request, in the body, do requests have all of the parameters for objects that they would be manipulating? Even if not all of them are changing? Or do they only send the fields that are being updated? So for example if we had a House object with No. of rooms and floors, and I was changing No. of rooms should I only send that parameter, or both of them?
If requests should only contain the fields that are being updating, then how would that translate to the DjangoRestFramework? Any help would be greatly appreciated!
My Views are:
class HouseDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = House.objects.all()
serializer_class = HouseSerializer
and serializer is:
class HouseSerializer(serializers.ModelSerializer):
quotes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = House
fields = (
'pk',
'address',
'quotes',
)
PUT is for full resource updates, PATCH is for partial resource updates because Fielding says so.
Therefore, in your example, if you wanted to only update a No. of rooms for your HouseDetail model, you would send the following payload in a PATCH request:
{ "no. of rooms": "42" }
It is still possible to partially update things with a PUT request in DRF (explained below) but you should simply use PATCH for that because it was created for this. You get PUT and PATCH functionality for your model because you have subclassed the generics.RetrieveUpdateDestroyAPIView class and have defined a serializer.
If a required field is omitted in your PUT request (see documentation for required), then you will receive a 400 status code with a response like {"detail": "x field is required"}. However, when you request via PATCH, there is a partial=True argument passed to the serializer which allows this partial only update. We can see this for the UpdateModelMixin:
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
which calls update, which is:
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)

How to restrict image file extension on Plone?

I have a Plone application in which I can upload images, which are ATImages. I want to validate the extension file (mainly to forbid pdf files). There are created with a url call like http://blablba.com/createObject?type_name=Image
I have tried setting the /content_type_registry with file extensions associated with images, with no success (pdf upload still work)
I guess I could write a new class extending ATImages, create a form with a validator, but it looks a little bit complicated and it seemed that some settings on content_type registry would be enough (or elsewhere).
How would you do that ? (forbid pdf ?)
thx
We had a similar problem.
Archetypes fires several events during its magic, amongst others a "post validation event" (IObjectPostValidation). This way we added a check for the content-type.
subscriber (zcml):
<subscriber provides="Products.Archetypes.interfaces.IObjectPostValidation"
factory=".subscribers.ImageFieldContentValidator" />
quick and dirty implementation:
from Products.Archetypes.interfaces.field import IImageField
from plone.app.blob.interfaces import IBlobImageField
from Products.Archetypes.interfaces import IObjectPostValidation
from zope.interface import implements
from zope.component import adapts
# import your message factory as _
ALLOWED_IMAGETYPES = ['image/png',
'image/jpeg',
'image/gif',
'image/pjpeg',
'image/x-png']
class ImageFieldContentValidator(object):
"""Validate that the ImageField really contains a Imagefile.
Show a Errormessage if it doesn't.
"""
implements(IObjectPostValidation)
adapts(IBaseObject)
img_interfaces = [IBlobImageField, IImageField]
msg = _(u"error_not_image",
default="The File you wanted to upload is no image")
def __init__(self, context):
self.context = context
def __call__(self, request):
for fieldname in self.context.Schema().keys():
field = self.context.getField(fieldname)
if True in [img_interface.providedBy(field) \
for img_interface in self.img_interfaces]:
item = request.get(fieldname + '_file', None)
if item:
header = item.headers
ct = header.get('content-type')
if ct in ALLOWED_IMAGETYPES:
return
else:
return {fieldname: self.msg}

Resources