Update the Mollom module to use a PHP client library for Mollom. Other CMSes like SilverStripe and Joomla! use the Mollom class by Tijs Verkoyen (see http://mollom.crsolutions.be) but they all have bugs. Therefore we want to maintain a class for other PHP frameworks to reuse. We'll take responsibility for making sure it is correct, and for making sure that it implements the latest and greatest APIs. The class should come with tests in mollom.test.
Todos
Done:
x Rename branch into 2.x.
x Change serversInit to rest.mollom.com
x Rename $siteId into $publicKey
x Rename $keyPublic into $publicKey (+ $keyPrivate)
x Add phpDoc.
x Add data types to phpDoc.
x Move class into includes
x Create 2.x dev snapshot
x Abstract request() handling into handleRequest()
x list parameters
x Add 'solved' and 'reason' to schema
x Move Mollom into $form_state
x Move local testing server into separate module
x OAuth v1 request signing
Needs backport:
x New CAPTCHA resource created upon POST, existing has to be checked first
x Make use of dhr() 'timeout' parameter, account for code 1: HTTP_REQUEST_TIMEOUT
Open:
- Add log(), getLog() methods
- Revamp logging.
- Remove $oAuthStrategy and OAuth 'query' option; only retain HTTP header support.
Needs feedback:
- Configuration CRUD methods? (When keeping, remove variable name mapping)
- Move mollom.drupal.inc into includes?
- Separate $query parameters from POST $data parameters? Not needed for current API calls. There's only a single $data parameter, which is either used for GET params or POST data. Long-term need depends on whether the API might allow for POST /foo?bar=baz including POST body/data (i.e., both GET and POST parameters).
Follow-ups for later:
- Add getIp() based on ip_address()
- Test testing mode
- Test 'solved' and 'reason'
- #ajax framework for switching captcha type
- Test captcha switching
- Remove MollomResellerTestCase entirely, should be covered by MollomServerResponsesTestCase already.
| Comment | File | Size | Author |
|---|---|---|---|
| #30 | mollom-rest.patch | 16.77 KB | netsensei |
| #24 | mollom.class_.24.patch | 257.44 KB | sun |
| #20 | mollom-class.20.patch | 189.25 KB | sun |
| #14 | mollom-class.14.patch | 182.21 KB | sun |
| #12 | mollom-class.12.patch | 176.43 KB | sun |
Comments
Comment #1
dries commentedBy the way, we'll also want lower-level tests for this library. Once we have this library, we can add support for a number of new Mollom APIs.
Comment #2
dave reidVery much agreed. Would be a great fit to bring this in and maintain it, while offering flexibility for other solutions to be able to override class methods. For instance a Mollom::request() or Mollom::xmlrpc() method that creates a HTTP request with cURL would be overridden in Drupal's implementation to use drupal_http_request() or xmlrpc().
Comment #3
sunBetter title.
Clicked on this issue for the second time, because I got mistaken by the issue title and thought "wait, at least D7 leverages PHP5" ;)
Comment #4
dave reidComment #5
dave reidSetting this to postponed until likely a 7.x-2.x branch. There are a lot of things I need to focus on first..
Comment #6
netsensei commentedHi,
I'm the maintainer of the Mollom Wordpress plugin (http://wordpress.org/extend/plugins/wp-mollom/). Over the past two years, I've maintained a stable branch of the plugin which was, well, mostly rapid prototyped. The way the plugin was built hampered development.
One of my biggest nags was the lack of a generic API which other plugin Wordpress could easily use. Wordpress doesn't have a concept of a unified form API like Drupal does, for one.
So, I've been busy working on a totally revamped version of the Wordpress version over the past months. While revising the entire architecture, I separated Wordpress specific code from my implementation of the Mollom API. A nice class was the net result. I was considering putting it up for peer review on Github as a separate project, until Dries asked me to post it here.
Anyway, the class is attached as a 'patch' with this comment.
How does it work?
Contrary to Tijs Verkoyens' class, this class doesn't come with a specific XML-RPC I/O base layer. I felt other developers should be able to use their preferred XML-RPC libraries instead of being restricted to a single specific implementation.
The class takes an instance of type MollomRPCClient and uses that instance for all it's I/O. The type is defined through an interface. If you use a stock XML-RPC library like the Incutio XML-RPC library, you'll need to write a wrapper class which implements this interface. In case of Drupal, this will mean implementing a wrapper class around the xmlrpc() function. (Maybe this can be done in a more efficient way. But this worked for me.)
This is how I bootstrap the class in my own plugin within a separate static factory function:
The factory function returns the $mollom object which is now ready to be used.
It would be great to join efforts. So feedback is welcome and I'm happy to answer questions!
Comment #7
netsensei commentedcorrects a typo in the setPrivateKey() function.
Comment #8
gábor hojtsyAgreed a generic mollom backend would be good. Unfortunately for mollomapi.module to be able to use this, it either needs to be in a separate module or the mollom module should not expose its usual UI.
Comment #9
sunI'm actively working on this, in combination with #1104292: Replace XML-RPC with REST API.
The code lives in the 7.x-class topic branch (mollom.inc) and is still undergoing major changes. Conversion started on a very low-level. Still contains lots of old/temporary code from an initial test conversion, including code for XML-RPC protocol that will be completely removed. Not really ready for review yet, but might provide some early clues on the rough edges.
Comment #10
sunmmm, ok, this is huge. :)
Might make more sense to look at the actual branch: http://drupalcode.org/project/mollom.git/tree/refs/heads/7.x-class
Comment #12
sunSome more clean-ups.
This is ready for review. The test failures cannot be resolved without further REST API adjustments.
That said, all of the Drupal tests are still running against the local dummy/fake REST server in this patch, because the production REST testing API differs too much from the specification.
Comment #14
sunMinor tweaks, resolving another @todo:
- Moved _mollom_get_version() into Mollom class.
- Fixed MollomDataTestCase.
Comment #16
dries commentedThis felt a bit unconventional. One would expect regular setters and getters. It's easier and probably less code too.
More to come.
Comment #17
sunOn configuration CRUD methods: There were individual read/write/delete methods earlier, but it was a lot of duplicate code. All Mollom client implementations for other platforms that I've looked into use and support a simple key/value store, so in the end, it felt needless to make these methods more granular than they need to be, forcing everyone to implement individual CRUD methods for every value when it's really about a trivial key/value configuration store only. Almost all clients will relay those operations to an existing component/subsystem in their system. An extremely simple implementation would merely use file_get_contents(), file_put_contents(), and unlink().
Comment #18
sunSpent some time to think about the exception, logging, and error code/message handling. When I changed query() and request() to use exceptions instead of passing around arbitrary error codes, the entire code became a lot cleaner and more predictable.
However, the surrounding code and logic for handling error codes and messages, as well as the internal logging of which requests were attempted, still remained and rather became more crufty, since throwing an exception halts the code execution. Therefore, I came up with this idea and asked around for any objections. None were raised, so I'm going to implement that change now.
Comment #19
sunAs intended and imagined, the idea heavily simplifies the internal error handling and logging. (commitdiff)
Looks like I forgot to mention that we have special needs for internal error handling in the Mollom class -- we need to track the "state" and possible errors in various locations within the class. Almost all errors and exceptions are not supposed to bubble up to the end-user; instead, the wrapping CMS plugin/module needs to react "nicely" in case troubles. Whereas possible troubles have varying severities - the client-side server balancing in the class may be able to recover from an error or bogus state, and in case it does not, the attempted recovery operations need to be logged, so a site administrator is able to provide these details in bug reports/support requests they send us.
Comment #20
sunUpdated cumulative patch.
Comment #21
corbacho commented.
Comment #23
sun@todo: Move
mollom.incintoincludes/mollom.class.incand put a README.txt next to it. Check whether it makes sense to also movemollom.drupal.incin there.Comment #24
sunFor the sake and hope of reviews, attached is a diff between 7.x-1.x and 7.x-2.x branches.
Comment #26
sunLet me iterate over the remaining @todos. Some of them are very clear already and just need to be implemented, but on some others, I'd appreciate any kind of feedback:
Let's add a very simple usage scenario here; e.g., checking a content for spam.
I think I've checked more than once in the meantime whether we can refine the exception classes, but they do actually trigger different behaviors in some calling methods. Thus, I think this @todo is obsolete.
While the observation and symptom is correct, the client actually has to perform the subsequent POST to update its client information. Without the preceding GET (in the edge-case of having no server list), it can't perform the POST. Thus, I think this @todo is obsolete, too.
Let's move the exception classes to the bottom of the file - it's wonky and distracting to see them first, instead of the main Mollom class.
These should be protected now. Public visibility was only needed for development and debugging.
I still think that these configuration CRUD methods are much easier to deal with for implementors of the class.
The only possible downside I could see is that mayhaps not all implementations are using or supporting a variable data store. With these configuration CRUD methods, they would have to manually track the possible configuration setting names and adjust their storage accordingly. Whereas with individual abstract loadPublicKey(), saveServers(), etc methods, the entire Mollom client class implementation would turn invalid in case of an API change or addition.
Happy to change these into individual methods if there's a strong opinion or reasoning to do so.
Regardless of that, I don't think these methods need to be public.
There should be a ->log() method for adding/recording log messages.
Still need to implement OAuth request signing. Working on it.
In case we will change the configuration CRUD methods into individual methods, then we need a solid and consistent naming pattern for methods; e.g.,
- loadFoo() to retrieve Foo from local storage,
- getFoo() to retrieve and prepare Foo in whatever means (which may or may not involve calling loadFoo())
Log message severities/types should follow a consistent pattern; i.e.,
- 'debug' for low-level request debugging data,
- 'info', 'notice', etc. for higher-level operation info.
Changing the default server in the static cache (not configuration) definitely makes sense.
However, we need to implement a "continuous" loop, which starts at the current index, and allows to cross the array end boundary (going back to start), until we reach the originally current index. Probably sounds more complicated than it is.
In addition, the Mollom backend team is going to investigate whether clients can be allowed to update the default server in their locally stored server list.
($next ? $next : '--')
Still not sure what to do about these special cases.
The 'list' case could probably be moved into ::query() (or even ::parseXML()), because it is generic.
The 'content']['languages' case is too specific though to be moved elsewhere.
That renaming suggestion still makes sense to me; getCaptcha() doesn't "get" a CAPTCHA image/audio file (also not using HTTP GET), it creates a CAPTCHA resource.
Just return FALSE here. (Same for other methods.)
Needs to be documented in @return.
I've to admit I didn't think about list parameters yet, since the Drupal module does not use them yet.
- 'offset' and 'count' can be passed in.
- 'listCount', 'listOffset', and 'listTotal' are returned (next to 'list').
The request parameters can be simple $offset and $count arguments to the method.
The list response parameters are a bit trickier though. Best idea I currently have would be to set them as public properties on the Mollom class, so in case you need them, your code looks like this:
Anyone any thoughts on that?
The suggestion to merge these two methods still makes sense to me. There was a "larger" difference when we used HTTP PUT, but now that there's only GET and POST, the two methods are almost identical, and it doesn't really matter the term is inserted or updated, as long as it's there. ;)
The JavaScript changes need manual testing.
Let's drop this mapping completely, add a database update, and simply use the setting names directly with a "mollom_" prefix; i.e., leading to:
The case-folding bug in Drupal core might prevent us from using uppercase letters in the names; in that case, simply convert the $name to lowercase.
This is one of the primary remaining @todos we should resolve.
Clients should only have to implement the actual code/logic to execute a HTTP request to Mollom, but they should not have to re-implement the logic to handle and "interpret" the response. By moving that code into the Mollom class, we ensure a consistent behavior across all client implementations.
Working on it.
Still not sure whether it would make sense to also move the Drupal implementation into includes. It's a nice example for how a real-world implementation should look like.
Still undecided, but rather leaning towards moving it into includes.
Still need to add those.
Good idea ;)
One of the most weird @todos, but yeah, we don't have tests for the testing mode yet. ;) Leaving that for later though.
Now that the code is in 7.x-2.x, let's separate the testing server into a separate testing module.
While being there, also remove the obsolete XML-RPC stuff.
Comment #26.0
sunUpdated issue summary.
Comment #27
sunI've resolved most of the todos outlined in #26 in the past days.
The issue summary contains a full list of summarized todos.
OAuth request signing is not pushed to 7.x-2.x yet, as I'm waiting for a couple of backend fixes.
Comment #27.0
sunUpdated issue summary.
Comment #27.1
sunUpdated issue summary.
Comment #27.2
sunUpdated issue summary.
Comment #27.3
sunUpdated issue summary.
Comment #28
netsensei commentedHi,
I've started playing around with the class. Implementation in a mollom.wordpress.inc class seems to be fairly easy. At the moment, I'm trying to get the verifyKey call to work. Anything beyond that should be fairly trivial.
I did get an error though: unknown function t() on line 604 in mollom.class.inc. Seems that there are four stray t() functions encapsulating a few strings which might get displayed either through Wordpress, Drupal or some other framework/app reusing the class.
I'm not sure about the saveConfiguration, deleteConfiguration and loadConfiguration functions. Handling of the public/private keycombo happens through the interface. The only use case I can see for those is caching the serverList. Unless you expect this to be extended to other values being (un)set in the future. Wordpress does have its' own get_option(), update_option() functions so no problems there.
The request() function is very useful. I've noticed Wordpress has improved the way HTTP calls can be made, so I'm now trying to get it to work with the wp_remote_get() and wp_remote_post() functions. So far, this looks promising, but I have to dig a bit deeper.
The best part of this approach is this: if the base class gets an update, as long as the abstract functions aren't touched, it should be just a matter of copy and paste without touching application specific code to keep things in sync.
Great work! :-)
Comment #29
netsensei commentedI've noticed that the verifyKeys call is geared towards the REST API while the query() method still uses the old XML RPC API. At the moment, the call redirects to http:///site/
instead of http://rest.mollom.com/v1/site/
Comment #30
netsensei commentedOkay. I've gone through the class and the tests and removed the XML-RPC stuff:
* Cleaned out the query() method. This might need some revision. As it stands, the server response tests pass.
* Added a public $restEndpoint property with a reference to http://rest.mollom.com
* Removed the refreshServers() method
* Removed the getServers() method
* Removed the MollomRefreshException class
* Removed the MollomRedirectException class
* Refactored the MollomDrupalTest accordingly
* Added variable_set('mollom_testing_mode', 1); to MollomWebTestCase to get the tests moving
I first tried to make $restEndpoint a class const first, but failed because overriding const is, well, not that easy. We coud use get_called_class() in query() to retrieve the correct const definition, but this would require the method to be declared static.
Comment #31
aspilicious commentedLook at your trailing whitespaces with dreditor or another tool.
Comment #32
sunUpfront: Note that the most recent and stable code lives in the 7.x-2.x-maas branch currently. We'll merge that back into 7.x-2.x very soon.
Thanks for spotting those - I just removed them.
Yeah, that has been idea and purpose of these simple key/value configuration helpers. Drupal equally has its own configuration store, so each application-specific implementation of the Mollom class can simply invoke its own/existing functions.
I'm a bit confused by that statement, since the most recent code contains nothing for the XML-RPC API anymore...?
The ->verifyKeys() method is a rather simple wrapper around ->updateSite(), passing client and version information to Mollom. It should be invoked when the API keys are added or changed for a site.
Can you check whether you get a proper server list returned in the ->refreshServers() method?
I'm not sure why you attempted to do this? :) The client-side server fallback mechanism is still used in the REST API, and one of the major goals for the generic PHP class is to ensure the correct logic for that for all Mollom client implementations ;)
Your client should only have to implement the ->request().
Would love to learn why you thought that these methods were no longer needed. Perhaps we need to adjust or improve the documentation? We already added some simple example lines to README.txt, but potentially we need some more technical details there.
Comment #33
netsensei commentedOh. I didn't check out the 7.x-2.x-maas branch but the 7.x-2.x branch. :-o So, that's why. Those changes were made against the code that lives in the latter branch.
I checked http://mollom.com/api/rest to see if there were major changes compared to the XML RPC version. I didn't find any reference to the fallback system so I removed it from the class.
Take the Site API for instance: http://mollom.com/api/rest#site The docs say that one should POST a call to POST http://rest.mollom.com/v1/site/{publicKey} to verify the keys. The class in the 7.x-2.x branch assembled this URL: http://rest.mollom.com/v1/123.456.789.000/site/{publicKey} where 123.456.789.000 is the IP address of the first server on the retrieved server list, and tried to send a POST request to it.
I did understand that the verifyKeys() is a wraps updateSite(). :-)
Since the REST docs don't say anything about refreshing the servers, I assumed that all calls now go directly to http://rest.mollom.com and load balancing happens on the server side from now on.
Anyhow, I should checkout the 7.x-2.x-maas branch and use the code which lives over there. Or I could wait until the two branches are merged. Enough todo's on the Wordpress plugin where I don't need the class atm. ;-)
Comment #34
sunI'm tentatively calling this issue fixed for now.
We'll move the generic PHP class to github today or tomorrow. (Doing some last-minute adjustments today.)
Comment #35
dave reid+1 for moving to a library for better re-use.
Comment #36
sunThe Mollom class has been extracted and pushed to https://github.com/Mollom/MollomPHP
Includes commit history. Some obvious minor clean-ups (e.g., readme) will happen shortly.
We'll also have to figure out how to manage some organizational things (e.g., versioning, testing, etc), but at least there's a unique origin now.
Comment #37.0
(not verified) commentedUpdated issue summary.