Extend Plone-Controlpanel Form - plone

Is it possible to extend the Controlpanel-View with each addon?
For Example
ca.db.core -> Makes basic fieldset/tab for DB Connection Settings
ca.db.person -> If installed, Adds to the "core" settings a new fieldset/tab for Person specific fields/settings
ca.db.schema -> If installed, also adds an new fieldset/tab for schema.org Fields

Yes it's possible, I once discussed this problem with a guy, who wrote the bda.plone.shop addon.
They faced the same problem, and solved it by using a ContextProxy object, which puts the different schema definitions together in one proxy object.
Using a proxy is IMHO a hack, but I don't know a better solution.
The proxy, tries to get/set a attribute from a list of schemas.
Be aware, you need to handle conflicting names, means if you have the same field name in more than one schema.
class ContextProxy(object):
def __init__(self, interfaces):
self.__interfaces = interfaces
alsoProvides(self, *interfaces)
def __setattr__(self, name, value):
if name.startswith('__') or name.startswith('_ContextProxy__'):
return object.__setattr__(self, name, value)
registry = getUtility(IRegistry)
for interface in self.__interfaces:
proxy = registry.forInterface(interface)
try:
getattr(proxy, name)
except AttributeError:
pass
else:
return setattr(proxy, name, value)
raise AttributeError(name)
def __getattr__(self, name):
if name.startswith('__') or name.startswith('_ContextProxy__'):
return object.__getattr__(self, name)
registry = getUtility(IRegistry)
for interface in self.__interfaces:
proxy = registry.forInterface(interface)
try:
return getattr(proxy, name)
except AttributeError:
pass
raise AttributeError(name)
Now you need to use the proxy in your ControlPanel form.
I assume you're using the RegistryEditForm from plone.registry:
class SettingsEditForm(controlpanel.RegistryEditForm):
schema = ISettings
label = _(u"Settings")
description = _(u"")
# IMPORTANT Note 1 - This is where you hook in your proxy
def getContent(self):
interfaces = [self.schema] # Base schema from ca.db.core
interfaces.extend(self.additionalSchemata) # List of additional schemas
return ContextProxy(interfaces)
# IMPORTANT Note 2 - You may get the additional schemas dynamically to extend the Settings Form. For example by name (startswith...)
# In this case they have a separate interface, which marks the relevant interfaces.
#property
def additionalSchemata(self):
registry = getUtility(IRegistry)
interface_names = set(record.interfaceName for record
in registry.records.values())
for name in interface_names:
if not name:
continue
interface = None
try:
interface = resolve(name)
except ImportError:
# In case of leftover registry entries of uninstalled Products
continue
if ISettingsProvider.providedBy(interface):
yield interface
...
You can find the full code here

Related

Groovy script to change all users to uppercase

I'm trying to change all user names with in a magnolia application to uppercase since we are having case sensitivity issues with our login.
I wrote this groovy script, following an example used to reset passwords to "", to capture the users and change them uppercase but it appears the name property is not being set.
https://documentation.magnolia-cms.com/display/WIKI/Reset+all+passwords
import info.magnolia.jcr.util.NodeUtil
import info.magnolia.jcr.predicate.NodeTypePredicate
import info.magnolia.jcr.util.NodeTypes
session = ctx.getJCRSession("users")
users = NodeUtil.collectAllChildren(session.getNode("/public"), new NodeTypePredicate(NodeTypes.User.NAME))
users.each() {
changedName = it.name.toUpperCase();
it.setProperty("name", changedName)
it.save();
println "1 " + changedName;
println "2 " + it.name;
}
session.save();
When I'm checking it.name it is return how they are stored in the mangolia, and not as all uppercase and they are also not being changed in the security app when looking at the username.
import info.magnolia.jcr.util.NodeUtil
import info.magnolia.jcr.predicate.NodeTypePredicate
import info.magnolia.jcr.util.NodeTypes
session = ctx.getJCRSession("users")
users = NodeUtil.collectAllChildren(session.getNode("/admin"), new NodeTypePredicate(NodeTypes.User.NAME))
users.each() {
name = it.name
changedName = it.name.toUpperCase();
it.setProperty("name", changedName)
it.setProperty("jcrName", changedName)
it.save()
NodeUtil.renameNode(it, changedName)
it.getNode("acl_users").getNodes().each { node ->
newPath = node.getProperty("path").getString().replace(name, changedName)
node.setProperty("path", newPath)
node.save()
}
}
session.save()
Hey, maybe this is what u are looking for. You need to change the Node name and the jcrName in my Version I iterate over the acl_users Node and change the path of each. Hope this works for you.
IIRC there's 3 things you need to change.
name is one of them, then there's jcrName property and then you need to change the name of the node itself. At least if you want to see it in security app that way.
For the login itself, what you did should be already sufficient.
Try to use the setProperty method of PropertyUtil.
You have to extract all nodes that you need and then loops through them. Supposing that the variable node is the Node you want to change the name, do this:
String newName = StringUtils.upperCase(PropertyUtil.getString(node, "jcrName"));
PropertyUtil.setProperty(node, "jcrName", newName);
jcrName is the property you need to overwrite. Wrap the code into a try/catch block and here we go.
Hope it helps.

Can a view define a __bobo_traverse__ method?

I am trying to make ARFilePreview work with newer versions of Plone. The product defines a view which has its own __bobo_traverse__ method. Unfortunately ZPublisher does not invoke this method.
The traverseName() method in BaseRequest.py has:
if IPublishTraverse.providedBy(ob):
ob2 = ob.publishTraverse(self, name)
else:
adapter = queryMultiAdapter((ob, self), IPublishTraverse)
if adapter is None:
## Zope2 doesn't set up its own adapters in a lot of cases
## so we will just use a default adapter.
adapter = DefaultPublishTraverse(ob, self)
ob2 = adapter.publishTraverse(self, name)
I would like it to take the else case but it results in a 404 because the if returns True.
Is there an easy fix?
Define a method called publishTraverse(same args as __bobo_traverse__) and invoke the existing __bobo_traverse__ from it.

Add new record to plone.registry without re-running GenericSetup / re-installing the product

in a Plone add-on product, I have a control panel page where some config options can be set. They are stored in plone.registry. The control panel adapter fetches the different fields in its __init__ method by querying the interface, like:
class MultiLanguageExtraOptionsAdapter(LanguageControlPanelAdapter):
implementsOnly(IMultiLanguageExtraOptionsSchema)
def __init__(self, context):
super(MultiLanguageExtraOptionsAdapter, self).__init__(context)
self.registry = getUtility(IRegistry)
self.settings = self.registry.forInterface(
IMultiLanguageExtraOptionsSchema)
Now I add an additional field to the interface IMultiLanguageExtraOptionsSchema and restart plone. On the control panel page I then an error:
KeyError: 'Interface `plone.app.multilingual.interfaces.IMultiLanguageExtraOptionsSchema` defines a field `blah`, for which there is no record.'
(This is expected for the forInterfacemethod , as described on the plone.registry README. The record is not there.)
Of course, if I add that field via GenericSetup (registry.xml), and I re-install the product / re-run the "Control Panel" step, all is well:
<registry>
<records interface="plone.app.multilingual.interfaces.IMultiLanguageExtraOptionsSchema">
<value key="blah"></value>
<records>
<registry>
But I don't want to force users to re-install a product, just because there's a new option in the product-specific control panel. So my question: Is there a recommended way for getting a new record for a new field into plone.registry?
You could try/catch the KeyError and then make sure all registry settings are registered:
try:
self.settings = self.registry.forInterface(IMultiLanguageExtraOptionsSchema)
except KeyError:
registry = getUtility(IRegistry)
registry.registerInterface(IMultiLanguageExtraOptionsSchema)
I would recommend to write an upgrade step though (which would force your users to reinstall the product of course).
upgrades.py:
def update_registry(context):
registry = getUtility(IRegistry)
registry.registerInterface(IMultiLanguageExtraOptionsSchema)
upgrades.zcml::
<genericsetup:upgradeStep
source="*"
destination="1100"
title="Update plone.app.multilingual setting registry"
description=""
profile="plone.app.multilingual:default"
handler=".upgrades.update_registry"
/>
See
https://github.com/collective/collective.mailchimp/blob/master/collective/mailchimp/upgrades.py
and
https://github.com/collective/collective.mailchimp/blob/master/collective/mailchimp/upgrades.zcml
for an example.
If you pass False as the second parameter to forInterface:
registry.forInterface(IMultiLanguageExtraOptionsSchema, False)
then it won't throw an error if fields from the schema are missing from the registry, but will simply return the field's default value.
Safe getting settings from registry:
def get_registry_settings(interface, name):
registry = getUtility(IRegistry)
settings = registry.forInterface(interface, check=False)
value = getattr(settings, name)
if value == settings.__schema__[name].missing_value:
value = settings.__schema__[name].default
return value

Table creation fails with SqlAlchemy and Pylons

I just upgraded to Pylons 1.0 and SqlAlchemy 0.6.5. What was a simple process of creating the DB schema no longer works.
I have a simple model:
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
user_name = Column(String)
def __init__(self, userName):
self.userName = userName
def __repr__(self):
return "<User('%s')>" % (self.userName)
When I run
paster setup-app development.ini
the database file is created (sqlite3), but not the table, and no errors are returned.
Logging shows that the following lines in websetup.py do execute:
log.info("Creating schema...")
Base.metadata.create_all(bind = Session.bind, checkfirst = True)
log.info("Database successfully set up.")
What am I missing?
Edit: Further digging shows that the Base.metadata.tables dictionary is empty. So, why isn't the model reflected in the metadata?
Ok, I found the issue.
In model.meta.py, Base = declarative_base() was already performed. When I also added that statement to the model.__init__.py, it evidently creates a new instance without the metadata, so there were no tables to create.
I'm not clear on exactly why/how this works, so if anyone (Mike Bayer?) has details, I would love to know.

relating models to one another using generic views

I'm new to Django and programming in general. I'm trying to make a simple site that allows players of a sport sign up for leagues that have been created by the admin. In my models.py, I created two models:
from django.db import models
from django.forms import ModelForm
class League(models.Model):
league_name = models.CharField(max_length=100)
pub_date = models.DateTimeField('date published')
class Info(models.Model):
league = models.ManyToManyField(League)
name = models.CharField(max_length=50)
phone = models.IntegerField()
email = models.EmailField()
def __unicode__(self):
return self.info
class InfoForm (ModelForm):
class Meta:
model = Info
exclude = ('league')
From what I've read, I can probably use the Create/Update/Delete generic views to display a form for the user to sign up for the league. So with my app, I want the user to come to a simple homepage that lists the leagues, be able to click on the league and enter their info to sign up. Here's what my urlconf looks like:
from django.conf.urls.defaults import *
from mysite.player_info.models import League, Info, InfoForm
info_dict = {
'queryset': League.objects.all(),
}
InfoForm = {'form_class' : InfoForm}
urlpatterns = patterns('',
(r'^$', 'django.views.generic.list_detail.object_list', info_dict),
(r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
url(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='player_info/results.html'), 'league_results'),
(r'^(?P<object_id>\d+)/info/create/$', 'django.views.generic.create_update.create_object', InfoForm),
)
Here's my problem: When I click on a league to sign up for on the homepage with my current setup, I get this error: TypeError at /league/1/info/create.... create_object() got an unexpected keyword argument 'object_id'. What am I doing wrong?
The issue isn't with your models, but rather with the function your "create" URL calls -- the line that calls django.views.generic.create_update.create_object() in urls.py. create_object() doesn't take an object_id argument, but you specified one in your url (r'^(?P<object_id>\d+)/info/create/$'). This makes sense -- you're creating an object, so you don't know its ID yet. create_object() only takes a form_class or model argument, as noted in the docs.
I'm guessing you're trying to create an Info object that is attached to a League object, and in that URL, <object_id> is the ID number of the League object; in which case, you shouldn't name that ID number, and instead should just use r"^\d+/info/create/$" as the URL. I'm not sure how you'll grab the league ID number using Django's create_object() function, though. You might have to write your own view handler. You may be able to use a custom ModelForm and pass it in with the form_class parameter, but I'm not sure.

Resources