CVS edit link for neilotoole

This is an application for a CVS account to support a new Drupal module 'elastic_email'. This module is code-complete.

For ease of evaluation, this submission is also available in HTML at:

http://neilja.net/elastic_email

The webpage is probably easier to parse than this message, and it also includes a downloadable package with full source code, screenshots, links to syntax-highlighted source code, etc.. If you choose to read the HTML page, you can safely ignore the rest of this message.

========
Introduction
========
This module directs outgoing Drupal email through the Elastic Email service, and can optionally queue messages for delivery at cron time.

Elastic Email is a mail relay service. That is, instead of your website sending mail via its own SMTP server, outgoing email is directed through the Elastic Email service and out onto the internet. Learn more at:

http://elasticemail.com.

This module is similar in concept to other third-party mail service modules such as Postmark, Mad Mimi, and Bronto.

Installation and use of this module is well documented in the README.txt available at:

http://neilja.net/elastic_email/src/README.txt

=======
Motivation
=======
Why this module? It provides integration with the Elastic Email third-party mail service. There is no other such module available.

Why Elastic Email?
------------------------
This is covered in more detail in README.txt, but basically:

Cloud-hosted (e.g. Amazon AWS) websites are frequently blacklisted by anti-spam organizations, and it is recommended to use a third-party mailing service.
Elastic Email is cheap, reliable, and incredibly easy to get up and running (less than 5 mins on a clean Drupal install.. really).

Maintenance?
------------------
This module is functionally complete right now. I do intend to maintain the module indefinitely, and will promptly address any bugs. The only future functionality being considered is support for sending attachments.

About the author?
-----------------------
I'm currently Architect Emeritus at Teradata Corporation, who are the market leaders (cf. Gartner) in data warehousing (i.e. they make the Bentley of databases). Amongst other duties, I run the Drupal-based Teradata Developer Exchange ( http://developer.teradata.com ). Prior to that, I was the Enterprise Portal & Identity Management Architect at SAIC, a huge government/DoD contractor. I am a member of the Java Community Process Expert Group for JSR-261, and a committing member of the Apache Software Foundation. I've been involved with open-source projects for years, e.g. with the NetTool web debugger and Apache Commons-Collections.

I am not affiliated with the providers of the Elastic Email service.

==============
Submission checklist
==============
This section addresses the common submission issues / questions noted in the Drupal.org submission guidelines documentation.

- Duplication: There is no other module providing the Email Service integration functionality. However, this module does also provide a mail-queueing mechanism, a la queue_mail. However, due to the fact that only a single module can implement drupal_mail_wrapper, it is not possible to leverage the queue_mail module. That said, the 'duplicated' code amounts to only a few lines.

- Completion: The module is code-complete and in production.

- Secure code: This is a 'system' module; there is no end-user interface. The only input fields are on the config screen, which is protected by the 'administer site configuration' permission. This module does not contain any SQL code of its own; the only interaction with the database is via the variable_XXX methods.

- t(): All string literals are passed through t().

- PHP code: there is no use of user-inserted PHP code.

- Coding standards: Coding standards are rigorously followed. Code Review via the 'Coder' module returns zero errors.

- Variable uninstall: All variables are removed via hook_uninstall().

- Module co-operation: This module can conflict with other mail-related modules that attempt to set 'smtp_library' (i.e. implements drupal_mail_wrapper()). The hook_requirements() code checks for this clash.

- HTML / themeing: There is no user-facing HTML generated that would require the use of themeing.

- JavaScript: This module does not supply any JavaScript code.

- License: A GPLv2 LICENSE.txt file is included in the module. No third-party files are part of of this module.

- Help: README.txt provides comprehensive documentation, and is also available from the Drupal help system.

Again, if you've read this far, it's probably much easier to read the HTML version of this at:

http://neilja.net/elastic_email

Any questions, please feel free to contact me at: neilotoole@apache.org

Comments

neilotoole’s picture

Status: Postponed (maintainer needs more info) » Needs review
StatusFileSize
new14.81 KB

Source tarball attached.

avpaderno’s picture

Issue tags: +Module review

Hello, and thank you for applying for a CVS account. I am adding the review tags, and some volunteers will review the code, pointing out what it needs to be changed.

sutharsan’s picture

Status: Needs review » Needs work

neilotoole,

Thanks for sharing your code. Your motivation of the module is clear and there is no module duplication indeed. You have done a good job documenting the code and using coder for code style. Well done! I have reviewed your module and have an number of comments to help you improve the module further and you help you learn to write better drupal code.

elastic_email.install
For localization you should for example use $t = get_t() to retrieve the appropriate localisation function name (t() or st()).
See: http://api.drupal.org/api/function/hook_requirements/6

elastic_email.module
Move the elastic_email_send() and _elastic_email_send() functions to the elastic_email.inc file. Including the in .module increases the memory footprint of *each* pageload while the functions are only used when sending out emails. Of course this change requires a module_load_include() in the admin module, but hey that's just admin.

The .module primarily contains hook implementations. Move these functions to the top of the file (after the defines).

elastic_email.inc
The number of code lines is fairly small of a separate module. As a rule of thumb a module should contain at least 50 lines. With the note above this minimum is met.

elastic_email.admin.inc
elastic_email_settings_form() // Introductory text
Use hook_help() for this explanation. This enables experienced users to turn off the help text.

t()
You break your lines of code at 80 chars. Breaking translatable text by inserting line endings is inconvenient for translators and may cause confusion. Don't use line breaks in translatable text if not required for the content (such as email body text).

Please modify your code and I will review it again.

avpaderno’s picture

t() is available when hook_install(), or hook_uninstall() are invoked. See the code executed from php_install() as example.

sutharsan’s picture

@kiamlaluno, the st() is required when the module is installed by an install profile. See the documentation of hook_requirements() for more info.

avpaderno’s picture

In hook_requirements() you need to use get_t(), but that is not true for hook_install(), or hook_uninstall(); in fact, php_install() uses t() directly.

neilotoole’s picture

StatusFileSize
new14.73 KB

Thanks for the kind words, and the thorough feedback.

>> elastic_email.install
>> For localization you should for example use $t = get_t() to retrieve the appropriate localisation function name (t() or st()).

I'm now using $t = get_t() for all of .install.

>> elastic_email.module
>> Move the elastic_email_send() and _elastic_email_send() functions to the elastic_email.inc file

Agreed, and done.

>> The .module primarily contains hook implementations. Move these functions to the top of the file (after the defines).

They floated to the top when I moved the impl code to elastic_email.inc.

>> elastic_email.admin.inc
>> elastic_email_settings_form() // Introductory text
>> Use hook_help() for this explanation. This enables experienced users to turn off the help text.

Agreed, and done.

>> t()
>> You break your lines of code at 80 chars.

t() strings are now unbroken.

>> Please modify your code and I will review it again.

Modified code attached. Many thanks.

neilotoole’s picture

Status: Needs work » Needs review

Guess I should change the status back to 'needs review'...

sutharsan’s picture

Status: Needs review » Needs work

I think these will be my last instructions, your doing fine Neil. I payed extra attention to safety this time.
The number of comments is higher, but maybe just because I had more time to spend ;)

Remove the license.txt file before you commit the package to CVS. The packaging script takes care of this.

elastic_email.info
Also, if the job_queue module is enabled, ...
This is not required to understand what the module does. The purpose of this text is a short general description, not a list of all features nor a sales pitch ;)
Remove this part or shorten it substantionally

elastic_email.install

$t('The variable "smtp_library" is set to !lib. This means another module is using another smtp_library. Be aware that this can cause emails not to be sent properly. Please unset the "smtp_library" manually, or disable the module that uses it, then disable and reenable this module.', array('!lib' => $lib)),

Although $lib (variable_get(DRUPAL_SMTP_LIBRARY, '');) is not likely to be unsafe, better be safe than sorry and use @lib or %lib instead.
smtp_library is usually a text string, so there is no harm in using @ or %
(!lib is used multiple times in this module)

return @drupal_get_path('module', 'elastic_email') . '/elastic_email.inc';

Is the at-sign required here?

function elastic_email_disable() {

  variable_set(DRUPAL_SMTP_LIBRARY, '');
}

If the admin, after reading "This means another module is using another smtp_library ..." decides not to use the Elastic Email module and disables it, the existing smtp_library setting (which may have been set by an other module) will get lost.
Only uset the value if it contains the elastic_email_library_path() value.

elastic_email.module

/**
* Implements hook_help().
*/

Missing spaces before the '*'.

elastic_email.admin.inc

t('To enable queueing, please install and enable the !link module.',
          array('!link' => l(t('job_queue'),
            'http://drupal.org/project/job_queue')))

HTML markup within translation strings is allowed, but should be avoided if possible. The exception are embedded links; link titles add a context for translators, so should be kept in the main string.
(See t() documentation) Use t("... Job Queue module ...", array('@job-queue-url' => 'http://drupal.org/project/job_queue'))

t("Unable to send test email because the 'site_email' variable is not set.");

This message is not realy usefull to an admin. Call it the Site's Email address and point to the relevant settings page (admin/settings/site-information)

$data = '<strong class="fail">'
  . t('Failed.')
  . '</strong> '
  . t('Reason:')
  . ' <tt>' . $result['error'] . '</tt>';

At first the unescaped $result['error'] seemed bad to me. But _elastic_email_send() outputs t() translated text in $result['error'].
Provide a line of comment to explain this.

elastic_email.inc

// If there's an error, log it.
watchdog('elastic_email', $result['error']);

The default watchdog severity is 'notice'. 'Error' is more appropriate here.

$result['error'] = t("Unable to send email because some required email parameters are empty.");

Use single quotes where possible. Single quotes have better performance.

$fp = @fopen('https://api.elasticemail.com/mailer/send', 'rb', FALSE, $ctx);

Although only used once, use a define for the URL "magic string".

$result['success']['msg'] = t(
  'Success [!tx_id]; message sent to: !recipients',
  array('!tx_id' => $response, '!recipients' => $to));

Are you sure $respose is safe? Better safe than sorry and use check_plain()

Code style
Check your code for trailing spaces (use Code module with Review set to "minor").

neilotoole’s picture

Status: Needs work » Needs review
StatusFileSize
new9.25 KB

@Sutharsan:

> I think these will be my last instructions

I'll believe it when I see it!! ;-)

All issues noted above have been addressed. Updated module attached.

sutharsan’s picture

Status: Needs review » Needs work

We're almost there ...

 $result['success']['msg'] = t(
  'Success [!tx_id]; message sent to: !recipients',
  array('!tx_id' => $response, '!recipients' => $to));

You have not convinced me that $response and $to are safe (see #9). Use @tx_id and @recipients instead.

[edit] changed the solution above.

neilotoole’s picture

Status: Needs work » Needs review
StatusFileSize
new9.27 KB

> Use @tx_id and @recipients instead.

Done. Updated module attached.

sutharsan’s picture

Status: Needs review » Reviewed & tested by the community

neiltoole, well done!

neilotoole’s picture

I know there's a backlog, but please let me know if there's anything that can be done to move the workflow along...

michelle’s picture

Status: Reviewed & tested by the community » Fixed

Approved based on Sutharsan's RTBC.

Michelle

Status: Fixed » Closed (fixed)
Issue tags: -Module review

Automatically closed -- issue fixed for 2 weeks with no activity.

avpaderno’s picture

Component: Miscellaneous » new project application
Issue summary: View changes
Status: Closed (fixed) » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.