{ "info": { "author": "Hans-Peter Locher", "author_email": "hans-peter.locher@inquant.de", "bugtrack_url": null, "classifiers": [ "Framework :: Plone", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License (GPL)", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "collective.pfg.silverpop\n========================\n\nAddon for PloneFormGen providing Integration\nof Silverpop enterprise newslettering.\n\nAdds a FormSilverpopAdapter which can be used\nto create newsletter signup forms to add a recipient\nto a Silverpop Newsletter.\n\nSupports Simple Opt-In /Opt-Out,\nmeaning that a user can sign in to a newletter,\nby checking a boolean field,\nand opt-out by unchecking the boolean field.\n\nRe Opt-In is not implemented yet.\n\nUses Silverpop Python API\n\nRequirements:\n\n - Products.PloneFormGen\n - Products.DataGridField\n - silverpop\n\nLinks:\n\n - PloneFormGen: http://pypi.python.org/pypi/Products.PloneFormGen\n - Silverpop: http://www.silverpop.com/\n - silverpop (Silverpop Python API): http://silverpop.googlecode.com\n - Code repository: http://svn.plone.org/svn/collective/collective.pfg.silverpop/\n\n\nChangelog\n*********\n\n0.9 (2009-05-18)\n----------------\n\n- change log levels,\n now logs to WARNING and ERROR\n [hplocher]\n\n0.8 (2009-05-14)\n----------------\n\n- handle URLError, HTTPError exceptions\n [hplocher]\n- display error message if an error occurs\n [hplocher]\n- fix issue (forms which had no custom fields\n did not lead to a silverpop api request)\n [hplocher]\n\n0.7 (2009-05-13)\n----------------\n\n- support opt in of an currently opted out\n recipient\n- use opt_in_recipient from silverpop\n- requires silverpop >= 0.4\n\n0.6 (2009-05-12)\n----------------\n\n- requires silverpop python package\n- refactored, use silverpop api methods from silverpop\n package\n [hplocher]\n\n0.5 (2009-05-11)\n----------------\n\n- add support for opt-in/opt-out functionality\n- define new policy: if the form contains a field with id\n 'silverpop_opt_in' we use this as control for opt in.\n If the field is True , the user is added to the newsletter.\n If it is False, the user will be opted our from the \n newsletter (e.g. usage boolean field checkbox checked=1, unchecked=0)\n [hplocher]\n- interpret xml response of silverpop [hplocher]\n- refactor test to create pretty xml output [hplocher]\n\n0.4 (2009-05-06)\n----------------\n\n- remove workflow for FormSilverpopAdapter [hplocher]\n\n- add functionality to define a custom 'field_id' ->\n 'silverpop_column_name' mapping [hplocher]\n\n- add a Mapping grid to the FormSilverPopAdapter:\n (id(readonly), title(readonly), silverpop api key)\n for configuring the mapping [hplocher]\n\n- requires DataGridField [hplocher]\n\n0.3 (2009-04-08)\n----------------\n\n- New policy: filter data fields by prefix. We're only using field names which\n start with COLUMN_NAME_PREFIX (**silverpop_**). This saves us from having field\n names which clash with plone IDs. Additionally, we've defined a mapping\n table for column names which are required verbatim as of the SilverPop API --\n COLUMN_MAPPING.\n [seletz]\n\n- Removed CONFIRMATION logic -- this can be handled better in PFG.\n [seletz]\n\n0.2 (2009-04-08)\n----------------\n\n- add unicode support,\n fixes #1 [Hans-Peter Locher]\n\n0.1 (2009-04-02)\n----------------\n\n- Initial release\n [Hans-Peter Locher]\n\nIntroduction\n************\n\nThis test shows how a PFG Form folder is added. We \nalso add our custom **FormSilverpopAdapter**, configure \nit and show how the actual sent XML looks.\n\nSetup\n-----\n\nFirst, we must perform some setup. We use the testbrowser that is shipped\nwith Five, as this provides proper Zope 2 integration. Most of the \ndocumentation, though, is in the underlying zope.testbrower package::\n\n >>> from Products.Five.testbrowser import Browser\n >>> browser = Browser()\n >>> portal_url = self.portal.absolute_url()\n\nThe following is useful when writing and debugging testbrowser tests. It lets\nus see all error messages in the error_log::\n\n >>> self.portal.error_log._ignored_exceptions = ()\n\nWith that in place, we can go to the portal front page and log in. We will\ndo this using the default user from PloneTestCase::\n\n >>> from Products.PloneTestCase.setup import portal_owner, default_password\n\n >>> browser.open(portal_url)\n\nWe have the login portlet, so let's use that::\n\n >>> browser.getControl(name='__ac_name').value = portal_owner\n >>> browser.getControl(name='__ac_password').value = default_password\n >>> browser.getControl(name='submit').click()\n\nHere, we set the value of the fields on the login form and then simulate a\nsubmit click.\n\nWe also set the roles we want to have::\n\n >>> self.setRoles(['Member', 'Manager'])\n\nWe monkeypatch silverpop to prohibit making requests to silverpop and just\ndisplay test output::\n\n >>> import silverpop\n >>> def opt_in_recipient(api_url, list_id, email, columns=[]):\n ... \"\"\"opt in a recipient to a list (only email key supported)\n ... api_url, list_id, email are required, optionally\n ... takes a list of dicts to define additional columns like\n ... [{'column_name':'State', 'column_value':'Germany'},]\n ... returns True or False\n ... \"\"\"\n ... print '**********silverpop-method***************************'\n ... print 'opt_in_recipient(api_url, list_id, email, columns=[])'\n ... print '**********attributes*********************************'\n ... print 'api_url: %s' % api_url\n ... print 'list_id: %s' % list_id\n ... print 'email: %s' % email\n ... print 'columns: %s' % columns\n ... return True\n\n >>> silverpop.opt_in_recipient = opt_in_recipient\n\n >>> def opt_out_recipient(api_url, list_id, email):\n ... \"\"\"opt out a recipient from a list\n ... api_url, list_id, email are required\n ... returns True or False\n ... \"\"\"\n ... print '**********silverpop-method****************'\n ... print 'opt_out_recipient(api_url, list_id, email)'\n ... print '**********attributes**********************'\n ... print 'api_url: %s' % api_url\n ... print 'list_id: %s' % list_id\n ... print 'email: %s' % email\n ... return True\n\n >>> silverpop.opt_out_recipient = opt_out_recipient\n\nWe also define a FakeRequest class to define our request\ncontaining just a form::\n\n >>> class FakeRequest(dict):\n ... def __init__(self, **kwargs):\n ... self.form = kwargs\n\nAdding content\n--------------\n\nAdd a new Form Folder::\n\n >>> browser.getLink('Form Folder').click()\n >>> browser.getControl('Title').value = 'testform'\n >>> browser.getControl('Save').click()\n\n >>> 'testform' in browser.contents\n True\n\nGo to the new Form Folder::\n\n >>> browser.getLink('testform').click()\n\n\nWe use the 'Add new' menu to add a new content item::\n\n >>> browser.getLink('Add new').click()\n\nThen we select the type of item we want to add. In this case we select\n'FormSilverpopAdapter' and click the 'Add' button to get to the add form::\n\n >>> browser.getControl('FormSilverpopAdapter').click()\n >>> browser.getControl(name='form.button.Add').click()\n >>> 'FormSilverpopAdapter' in browser.contents\n True\n\nNow we fill the form and submit it::\n\n >>> browser.getControl(name='title').value = 'testadapter'\n >>> browser.getControl('Silverpop API URL').value = 'http://url.com'\n >>> browser.getControl('Silverpop List Id').value = '1'\n >>> browser.getControl('Save').click()\n >>> 'Changes saved' in browser.contents\n True\n\nWe added a new 'FormSilverpopAdapter' content item to the testform.\n\nField Name Policy\n-----------------\n\nWe enforce the following policies regarding the field names which we send to\nSilverPop via their API:\n\n- field names MUST start with a common perfix: \"silverpop\\_\"\n- there must be one field \"silverpop_email\"\n- there can be an additional field \"silverpop_opt_in\"\n to control opt-in/opt-out\n\nWe have a transformation function which does that::\n\n >>> from collective.pfg.silverpop.utilities import transform_column_name\n >>> transform_column_name(\"silverpop_foo\")\n 'foo'\n >>> transform_column_name(\"no_prefix\") is None\n True\n\nonSuccess\n---------\n\nOn submit of the form, the onSuccess method of\nour FormSilverpopAdapter will be called.\n\nWe want to access our testform and testadapter directly::\n\n >>> self.testform = self.portal.testform\n >>> self.testadapter = self.portal.testform.testadapter\n\nWe create some fields inside our form.\n\nFirst, we create fields which should be regarded by our 'FormSilverpopAdapter'.\n\nThe special 'sivlerpop_email' field (this field has a fixed mapping)::\n\n >>> self.testform.invokeFactory('FormStringField', 'silverpop_email', title='Email')\n 'silverpop_email'\n\nA field to insert a name::\n\n >>> self.testform.invokeFactory('FormStringField', 'silverpop_name', title='Name')\n 'silverpop_name'\n\nWe also create a misc field, which shouldn't be regarded, although \nit is in the same form::\n\n >>> self.testform.invokeFactory('FormStringField', 'credits_to_admin', \\\n ... title='Give your credits to the admin of the site')\n 'credits_to_admin'\n\nUSER DEFINED MAPPING\n++++++++++++++++++++\n\nWe offer the ability, to define a mapping from\nfield ids to Silverpop API Keys.\n\nNO MAPPING\n..........\n\nFirst, we check what happens when we don't change the mapping.\n\nWe go to the 'FormSilverpopAdapter' s edit form::\n\n >>> browser.open(portal_url+'/testform/testadapter/edit')\n\nThe current mapping should only contain one record\n(with columns id, title, silverpop api key),\nso we check all columns.\n\nid::\n\n >>> browser.getControl(name='mapping.id:records').value\n 'silverpop_name'\n\ntitle::\n\n >>> browser.getControl(name='mapping.title:records').value\n 'Name'\n\nsilverpop api key::\n\n >>> browser.getControl(name='mapping.silverpop api key:records', index=0).value\n ''\n\nWe set up the list of fields::\n\n >>> fields = [self.testform.silverpop_email, \\\n ... self.testform.silverpop_name, self.testform.credits_to_admin,]\n\nWe set up a minimal request, containing the user's input::\n\n >>> request = FakeRequest(silverpop_email='x@x.com', silverpop_name='Hans', \\\n ... credits_to_admin='I like the high availability')\n\nWe now call the adapter's onSuccess method::\n\n >>> self.testadapter.onSuccess(fields,request)\n **********silverpop-method***************************\n opt_in_recipient(api_url, list_id, email, columns=[])\n **********attributes*********************************\n api_url: http://url.com\n list_id: 1\n email: x@x.com\n columns: [{'column_value': 'Hans', 'column_name': 'name'}]\n\nCUSTOM MAPPING\n..............\n\nNow, we check what happens when we change\nthe mapping.\nWe want to use 'FIRST NAME' as COLUMN \nfor silverpop, for the 'silverpop_name'\nfield.\n\nWe go to the 'FormSilverpopAdapter' s edit form::\n\n >>> browser.open(portal_url+'/testform/testadapter/edit')\n\nWe change the value inside the mapping::\n\n >>> browser.getControl(name='mapping.silverpop api key:records', index=0).value = 'FIRST NAME'\n >>> browser.getControl('Save').click()\n\nThe list of fields, is still the same.\n\nWe set up a minimal request, containing the user's input::\n\n >>> request = FakeRequest(silverpop_email='x@x.com', silverpop_name='Hans', \\\n ... credits_to_admin='I like the high availability')\n\nWe now call the adapter's onSuccess method::\n\n >>> self.testadapter.onSuccess(fields,request)\n **********silverpop-method***************************\n opt_in_recipient(api_url, list_id, email, columns=[])\n **********attributes*********************************\n api_url: http://url.com\n list_id: 1\n email: x@x.com\n columns: [{'column_value': 'Hans', 'column_name': 'FIRST NAME'}]\n\nOPT-IN/OPT-OUT\n++++++++++++++\n\nWe create a boolean field with the special id 'silverpop_opt_in'\nin our form::\n\n >>> self.testform.invokeFactory('FormBooleanField', 'silverpop_opt_in', \\\n ... title='Yes I want to get the newsletter')\n 'silverpop_opt_in'\n\nThis field mustn't occur in the testadapter's mapping grid.\n\nWe go to the 'FormSilverpopAdapter's edit form::\n\n >>> browser.open(portal_url+'/testform/testadapter/edit')\n\nThe current mapping should still only contain one record, so\nwe check the id column::\n\n >>> browser.getControl(name='mapping.id:records').value\n 'silverpop_name'\n\nOPT-IN\n......\n\nThe user wants to get the newsletter. A checked boolean field\nwill lead to a 'True' in the request.\nWe set up a minimal request, containing the user's input::\n\n >>> request = FakeRequest(silverpop_email='x@x.com', silverpop_name='Hans', \\\n ... silverpop_opt_in='True')\n\nThe list of fields now looks as follows::\n\n >>> fields = [self.testform.silverpop_email, self.testform.silverpop_name, \\\n ... self.testform.silverpop_opt_in]\n\nWe now call the adapter's onSuccess method::\n\n >>> self.testadapter.onSuccess(fields,request)\n **********silverpop-method***************************\n opt_in_recipient(api_url, list_id, email, columns=[])\n **********attributes*********************************\n api_url: http://url.com\n list_id: 1\n email: x@x.com\n columns: [{'column_value': 'Hans', 'column_name': 'FIRST NAME'}]\n\nOPT-OUT\n.......\n\nThe user doesn't want to get the newsletter, or\ndoesn't want it anymore. An unchecked boolean field\nwill lead to a 'False' in the request.\n\nWe set up a minimal request, containing the user's input::\n\n >>> request = FakeRequest(silverpop_email='x@x.com', silverpop_name='Hans', \\\n ... silverpop_opt_in='False')\n\nThe list of fields is still the same.\n\nWe now call the adapter's onSuccess method::\n\n\n >>> self.testadapter.onSuccess(fields,request)\n **********silverpop-method****************\n opt_out_recipient(api_url, list_id, email)\n **********attributes**********************\n api_url: http://url.com\n list_id: 1\n email: x@x.com\n\nContributors\n************\n\nHans-Peter Locher, Author\nStefan Eletzhofer\n\n\nDownload\n********", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://svn.plone.org/svn/collective/collective.pfg.silverpop/tags/0.9", "keywords": "Plone PloneFormGen Silverpop", "license": "GPL", "maintainer": null, "maintainer_email": null, "name": "collective.pfg.silverpop", "package_url": "https://pypi.org/project/collective.pfg.silverpop/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/collective.pfg.silverpop/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://svn.plone.org/svn/collective/collective.pfg.silverpop/tags/0.9" }, "release_url": "https://pypi.org/project/collective.pfg.silverpop/0.9/", "requires_dist": null, "requires_python": null, "summary": "Addon for PloneFormGen providing Silverpop integration", "version": "0.9" }, "last_serial": 788001, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "58017c917a97d226025d6b9855e4119b", "sha256": "eb20f3f68d44def558bd766bb3b3f8749358a05a0b2dd2364fa742466f07947f" }, "downloads": -1, "filename": "collective.pfg.silverpop-0.1.tar.gz", "has_sig": false, "md5_digest": "58017c917a97d226025d6b9855e4119b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21217, "upload_time": "2009-04-02T17:30:35", "url": "https://files.pythonhosted.org/packages/e4/c0/4bdfb6d3ed5a921b9f5e463389d05de183a4da3eb832817cbf7b634c688b/collective.pfg.silverpop-0.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "24bd70cf55ee45828523b35c344dc39d", "sha256": "a62eb7d2959c6ba262f39fe2c394afe36d55f2f608c48053dd3971beaaf45d37" }, "downloads": -1, "filename": "collective.pfg.silverpop-0.2.zip", "has_sig": false, "md5_digest": "24bd70cf55ee45828523b35c344dc39d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37337, "upload_time": "2009-04-08T11:28:38", "url": "https://files.pythonhosted.org/packages/1a/f5/76101454600d76272601289384f651e89835d9c48bfbc34582c921fbc77c/collective.pfg.silverpop-0.2.zip" } ], "0.3": [ { "comment_text": "", "digests": { "md5": "d899bb56f91c6f58d794ece9892d73ff", "sha256": "f1ed9189f35f0cc5e677e211ba32cae605da41574d5f256462739f80e1bb2b69" }, "downloads": -1, "filename": "collective.pfg.silverpop-0.3.zip", "has_sig": false, "md5_digest": "d899bb56f91c6f58d794ece9892d73ff", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 38626, "upload_time": "2009-04-08T19:05:10", "url": "https://files.pythonhosted.org/packages/e7/d1/1741f93d087b949f7cb24d04d79ad4856f9fbdcce5bf2d4b7ecfa4b6ee84/collective.pfg.silverpop-0.3.zip" } ], "0.4": [ { "comment_text": "", "digests": { "md5": "f810d26fe621a851efd2f64dc87fd624", "sha256": "649fa231f9c168d0c12b23738f0d3aaf69c87f72a29068cad72f0c62669a4aa4" }, "downloads": -1, "filename": "collective.pfg.silverpop-0.4.zip", "has_sig": false, "md5_digest": "f810d26fe621a851efd2f64dc87fd624", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 44924, "upload_time": "2009-05-06T17:44:32", "url": "https://files.pythonhosted.org/packages/e1/3a/b8ca0c901af472240dd5899c56a52fcb1e2fa082ca5b2a658d3454851a41/collective.pfg.silverpop-0.4.zip" } ], "0.5": [ { "comment_text": "", "digests": { "md5": "99fa288736bb86dcb17ed10ad7cf6f5e", "sha256": "f57aa216e476eda4c47d9a2e51b6f6021126f033f63e702d4624b526aca2743f" }, "downloads": -1, "filename": "collective.pfg.silverpop-0.5.zip", "has_sig": false, "md5_digest": "99fa288736bb86dcb17ed10ad7cf6f5e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 45034, "upload_time": "2009-05-11T16:18:17", "url": "https://files.pythonhosted.org/packages/f6/d0/ef1079f939608935441d7d1e906f6da1d69b101a299607d92f9673607d91/collective.pfg.silverpop-0.5.zip" } ], "0.6": [ { "comment_text": "", "digests": { "md5": "f2a8bd92bff86d2dbd277c7795165107", "sha256": "af483bf34ec6a81b1cd33c124a538382d80c77e189cdfcaf32e69f66ba28d8d8" }, "downloads": -1, "filename": "collective.pfg.silverpop-0.6.zip", "has_sig": false, "md5_digest": "f2a8bd92bff86d2dbd277c7795165107", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 44795, "upload_time": "2009-05-12T09:17:04", "url": "https://files.pythonhosted.org/packages/76/48/3a81a57186d0b01333fa73b5f4b97c90368c4a776d570408dbef5cc6053e/collective.pfg.silverpop-0.6.zip" } ], "0.7": [ { "comment_text": "", "digests": { "md5": "598222bccece00fedb14a5b62a70c7be", "sha256": "83cf0bca8806bda7365cc5f911be7332904632cf63ebf8184ce9c25bfce9b53b" }, "downloads": -1, "filename": "collective.pfg.silverpop-0.7.zip", "has_sig": false, "md5_digest": "598222bccece00fedb14a5b62a70c7be", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 44666, "upload_time": "2009-05-13T16:10:41", "url": "https://files.pythonhosted.org/packages/06/c2/19c99a663fac2c96278501341400da87cdc722ac628e46c4ec49eb53c5dc/collective.pfg.silverpop-0.7.zip" } ], "0.8": [ { "comment_text": "", "digests": { "md5": "f60550b7cc3d342ef88536d9eebb5eb1", "sha256": "d2aebefca1dc8369f87613a5570f170185bd4d50548b410962e1a942605c9a7a" }, "downloads": -1, "filename": "collective.pfg.silverpop-0.8.zip", "has_sig": false, "md5_digest": "f60550b7cc3d342ef88536d9eebb5eb1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 45172, "upload_time": "2009-05-14T12:21:05", "url": "https://files.pythonhosted.org/packages/f2/6d/4372764446e533179f0e2d19e926db5c6550e6656f31466fa251c2853447/collective.pfg.silverpop-0.8.zip" } ], "0.9": [ { "comment_text": "", "digests": { "md5": "8eb513980c11edcca053f30f9e94904a", "sha256": "e799e8efce3d4b31307ea94b6b7e6c1cc3c536ac15d9016af66744221c619e24" }, "downloads": -1, "filename": "collective.pfg.silverpop-0.9.zip", "has_sig": false, "md5_digest": "8eb513980c11edcca053f30f9e94904a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 45462, "upload_time": "2009-05-18T16:36:58", "url": "https://files.pythonhosted.org/packages/bf/5d/a9f9e1e2c4888feceb7db0bf04ae59b7a58fa49438f139b6c617cc75e80f/collective.pfg.silverpop-0.9.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8eb513980c11edcca053f30f9e94904a", "sha256": "e799e8efce3d4b31307ea94b6b7e6c1cc3c536ac15d9016af66744221c619e24" }, "downloads": -1, "filename": "collective.pfg.silverpop-0.9.zip", "has_sig": false, "md5_digest": "8eb513980c11edcca053f30f9e94904a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 45462, "upload_time": "2009-05-18T16:36:58", "url": "https://files.pythonhosted.org/packages/bf/5d/a9f9e1e2c4888feceb7db0bf04ae59b7a58fa49438f139b6c617cc75e80f/collective.pfg.silverpop-0.9.zip" } ] }