justb3a

Facebook Events

Recommended Posts

FacebookEvents

ProcessWire module to get Facebook Page Events using the Graph API.

Create an app on Facebook developers website

You have to create an app to get appId and appSecret. Those keys are required.
Go to Facebook Developers and add a new app.
Congrats! Now you can copy your Facebook appId and appSecret.

Get your Facebook page ID

You can either enter your facebook page ID or the facebook page name.
If you enter the Facebook page name, this module will get the page ID for you!

Call Module

$events = $modules->get('FacebookEvents')->getEvents();

Output Events

{% for event in events|reverse %}
  {% if event.start_time|date('U') > date().timestamp %}
    <div>
      {% set dts = modules.get('FacebookEvents').getDates(event) %}
      <a href="https://www.facebook.com/events/{{event.id}}/" title="Facebook">{{dts.dates}}:</a>
      {{event.name}} <em>{{dts.times}}</em>
    </div>
  {% endif %}
{% endfor %
  • Like 21

Share this post


Link to post
Share on other sites

At the moment the endpoint just returns: id, name, timezone, start_time, end_time, description.

But it's pretty easy to get the information about the place and cover. I updated the module to include it as well and updated the Graph Api Version to 2.9.

`$modules->get('FacebookEvents')->getEvents()` returns now the following data:

array(1) {
  [0]=>
  array(6) {
    ["id"]=>
    string(15) "123123123"
    ["name"]=>
    string(3) "xxx"
    ["timezone"]=>
    string(13) "Europe/Berlin"
    ["start_time"]=>
    string(24) "2017-06-23T16:00:00+0200"
    ["end_time"]=>
    string(24) "2017-06-23T18:30:00+0200"
    ["description"]=>
    string(3) "foo",
    ["place"]=>
    array(3) {
      ["name"]=>
      string(5) "Venue"
      ["location"]=>
      array(6) {
        ["city"]=>
        string(7) "Berlin"
        ["country"]=>
        string(7) "Germany"
        ["latitude"]=>
        float(xx.3321915)
        ["longitude"]=>
        float(xx.3735704)
        ["street"]=>
        string(17) "Street 14"
        ["zip"]=>
        string(5) "01234"
      }
      ["id"]=>
      string(15) "123123123"
    }
    ["cover"]=>
    array(4) {
      ["offset_x"]=>
      int(0)
      ["offset_y"]=>
      int(85)
      ["source"]=>
      string(148) "https://scontent.xx.fbcdn.net/v/t31.0-8/s720x720/..."
      ["id"]=>
      string(16) "123123123"
    }
  }
}

 

Having a quick look at the Graph API documentation, using the event ID one could send another request to get even more data. 
Please read the Graph API documentation at https://developers.facebook.com/docs/graph-api/reference/event/.

  • Like 2

Share this post


Link to post
Share on other sites

ok thx justb3a
i'll try that.

could you help me with outputing the dates. i'm a php-beginner and can't output the events. your code above is written in twig, is that an engine?
anyway i tried it like this:

<?php
	$events = $modules->get('FacebookEvents')->getEvents();
		echo "<ul>";
			foreach($events as $event) {
				echo "<li>$event->name</li>";
				}
		echo "</ul>";
?>

but i don't get any ouput from this.
 

maybe i did something wrong while creating the facebook-app. i added the ID and SECRET into the module and the page-name. but the module doesnt add the page-ID automatically!
do i have to be the owner of the facebook-page which events i want to get?

Share this post


Link to post
Share on other sites

The workflow is as follows:

  1. Enter Client ID and Secret
  2. Enter Page Name OR Page ID: If you enter the Page Name, make sure to copy it from the Facebook page url similar to `https://www.facebook.com/NameWithoutSpaces` => NameWithoutSpaces would be the Page Name
  3. Add `$events = $modules->get('FacebookEvents')->getEvents();` to a template and open the belonging page (frontend)
  4. Go back to module settings and check (reload before) if a page id has been added. If not, your page name could not be resolved.
  5. Now you should be able to receive events:
$events = $modules->get('FacebookEvents')->getEvents();
echo "<ul>";
foreach ($events as $event) {
  echo "<li>{$event['name']}</li>";
}
echo "</ul>";

$events is an array, so you have to use the following syntax: `$event['propertyName']`.

I found a little bug and updated the module, so please make sure to get the latest version.

  • Like 2

Share this post


Link to post
Share on other sites
3 hours ago, justb3a said:

$events is an array, so you have to use the following syntax: `$event['propertyName']`.

that was it!

thx, great module!
there should be a „buy-me-a-beer“-button (flattr, paypal, …)!

Share this post


Link to post
Share on other sites

me again,

everything worked fine. at first the cache was set to now. then i changed that to daily and moved the website to another directory. since then module doesn't get newly added events. it seems the old data is stored somewhere an no updates are possible.

i created a new app and tried it different APP-ID and had no luck.
i still get data output when the APP-ID, APP-SECRET and Facebook-Name-Fields are empty !? and i don't use any other cache-modules

am i doing something wrong?

Share this post


Link to post
Share on other sites

Have a look at your caches table. Search for name=Module.FacebookEvents. Delete this row.

Furthermore you could have a look at your modules table where class=FacebookEvents, column: data. 

  • Like 1

Share this post


Link to post
Share on other sites

it suddenly works now, i think the 24h were over since the last update.

i'll have look at the tables next time. thx

Share this post


Link to post
Share on other sites

ah

changed line:29 in FacebookEvents.module to:

const FB_PAGEEVENTS_URL = '%sv%s/%s/events/?fields=%s&access_token=%s&since=%s&until=%s&limit=50';

 

Share this post


Link to post
Share on other sites

Hi,

this is all working fine for me, nice module!

I have one question: is it also possible to display the image of the event?

I think i should use "Cover"  and "Source" but i'm not that good with php so I don't know in what sentence to use it..

Share this post


Link to post
Share on other sites

@ngrmm It's never a good idea to override somehing in the core or in a third-party module because if you update it your changes are overritten again. I updated the module and added an option in module settings to set the limit.

@Tacolombardi

<?php $events = $modules->get('FacebookEvents')->getEvents(); ?>
<ul>
  <?php foreach ($events as $event): ?>
    <li>
      <?=$event['name']?>
      <img src="<?=$event['cover']['source']?>" alt="<?=$event['name']?>" />
    </li>
  <?php endforeach; ?>
</ul>

 

  • Like 3

Share this post


Link to post
Share on other sites
On 1.6.2017 at 8:32 AM, justb3a said:

Have a look at your caches table. Search for name=Module.FacebookEvents. Delete this row.

Furthermore you could have a look at your modules table where class=FacebookEvents, column: data. 

still having problems. newly added events won't show up in frontend. i guess this has something to do with cache/tables.
i have to delete it manually, is there a way the module would do that automatically?

Share this post


Link to post
Share on other sites

What i did was to make the Page ID field editable, that was the only way to test the module with a dummy events feed and then switch it out to a different page; the clear module cache select item is not working for some reason...

in line 66 of the FacebookEventsConfig.php you can comment out this:

$field->collapsed = Inputfield::collapsedNoLocked;

fb_events1.thumb.jpg.10036a3e8744f370d4db601b86f025e6.jpg

...

:(

...

fc_events2.thumb.jpg.213dbdf62a2e2c4f176a1886a94e93af.jpg

I made some other changes to the module to allow the users to configure the since date, and until date; Once that's all stable i'll hopefully do a pull request on github for the extended features; and if i can figure out how to make it update the ID of the page if the cache is set to expire now, i'll post back here..

Share this post


Link to post
Share on other sites

@Macrura Are you using the latest version? I fixed a little bug just a few weeks ago. Looking forward to merge your PR :P

Share this post


Link to post
Share on other sites

What about this module makes to specific to PW 3.x and above? I would like to be able to use it on site using PW 2.7.2.

Share this post


Link to post
Share on other sites
15 minutes ago, Lance O. said:

What about this module makes to specific to PW 3.x and above? I would like to be able to use it on site using PW 2.7.2.

This module uses namespaces which has been introduced with PW 3. I upgraded all my projects to PW 3.x, so I have no need to test and support older versions. :) 

  • Like 1

Share this post


Link to post
Share on other sites

This may be a dumb question, but how do I get past events?

With normal usage I just get the future events, but when I modify the module at the getEvents() function and set the $sinceDate to 0, I get all past events aswell.

Am I missing an implemented solution intended by @justb3a?

Share this post


Link to post
Share on other sites

ok cool -thanks for testing out my version!

i will be submitting a PR soon so once that happens and if it is accepted you could switch back to the official branch; but if it works for you and satisfies your current requirements, it should be 'stable', there are just a few simple changes to the code and those added inputfields. if you do run into any issues, you can post an issue on the forked github issues page..

Share this post


Link to post
Share on other sites
On 28/10/2017 at 5:39 PM, Macrura said:

i will be submitting a PR soon so once that happens and if it is accepted you could switch back to the official branch; but if it works for you and satisfies your current requirements, it should be 'stable', there are just a few simple changes to the code and those added inputfields. if you do run into any issues, you can post an issue on the forked github issues page..

I created a "Pull Request" at my own and extended the module config, so one is able to define a date range and reverse sorting. Thanks @Macrura

  • Like 4

Share this post


Link to post
Share on other sites

all of a sudden module stopped working. i do not use any caching. i'm a bit puzzeld what happened
any changes from facebook?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By noelboss
      Page Query Builder
      Build complex nested queries containing multipple fields and pages and return an array that can be parsed to JSON. This is usefull to fetch data for SPA and PWA.
      You can use it to tranfrom a ProcessWire Page or PageArray, even RepeaterMatrixPageArrays into an array or JSON. Queries can be nested and contain closures as callback functions.
      Installation
      Via ProcessWire Backend
      It is recommended to install via the ProcessWire admin Modules > Site > Add New > Add Module from Directory using the PageQueryBuilder class name.
      Manually
      Download the files from Github or the ProcessWire repository: https://modules.processwire.com/modules/page-query-builder/
      Copy all of the files for this module into /site/modules/PageQueryBuilder/ Go to “Modules > Refresh” in your admin, and then click “install” for the this module. Methods
      There are two main methos:
      Return JSON
      $page->pageQueryJson($query); Return an Array
      $page->pageQueryArray($query); Building the query
      The query can be with key value pairs, or only keys. and can be nested. To ilustrate a short example:
      // simple query: $query = [ 'height', 'floors', ]; $pages->find('template=skyscraper')->pageQueryJson($query); Queries can be nested, call children etc:
      // simple query: $query = [ 'height', 'floors', 'images', // < some fileds contain default sub-queries to return data 'files' => [ // but you can also overrdide these defaults: 'filename' 'ext', 'url', ], // Assuming there are child pages with the architec template, or a // field name with a page relation to architects 'architect' => [ // sub-query 'name', 'email' ], // queries can contain closure functions 'querytime' => function($parent){ return "Query for $parent->title was built ".time(); } ]; $pages->find('template=skyscraper')->pageQueryJson($query); Keys:
      A single fieldname; height or floors or architects 
      The Module can handle the following fields:
      Strings, Dates, Integer… Page references Pageimages Pagefiles PageArray MapMarker FieldtypeFunctional A template name; skyscraper or city
      The fieldname of a child (child.name); url or filename or title
      A ProcessWire selector; template=building, floors>=25
      A new name for the returned index passed by a # delimiter:
      $query = [ 'skyscraper' => [ 'height', 'floors' ], 'architect' => [ 'title', 'email' ], ]  
      Key value pars:
      Any of the keys above with an new query array:
      $query = [ 'skyscraper' => [ 'height', 'floors' ], 'architect' => [ 'title', 'email' ], ]  
      A key and a closure functions to process and return a query. The closure gets the parent as argument:
      $query = [ 'architecs' => function($parent) { $architects = $parent->find('template=architect'); return $architects->arrayQuery(['name', 'email']); // or return $architects->explode('name, email'); } ] Real life example:
      $query = [ 'title', 'subtitle', // naming the key invitation 'template=Invitation, limit=1#invitation' => [ 'title', 'subtitle', 'body', ], // returns global speakers and local ones... 'speakers' => function($page){ $speakers = $page->speaker_relation; $speakers = $speakers->prepend(wire('pages')->find('template=Speaker, global=1, sort=-id')); // build a query of the speakers with return $speakers->arrayQuery([ 'title#name', // rename title field to name 'subtitle#ministry', // rename subtitle field to ministry 'links' => [ 'linklabel#label', // rename linklabel field to minlabelistry 'link' ], ]); }, // ProcessWire selector > name result "location" 'template=Location, limit=1#location' => [ 'title#city', // summary title field to city 'body', 'country', 'venue', 'summary#address', // rename summary field to address 'link', // ticket link 'map', // Mapmarker field, automatically transformed 'images', 'infos#categories' => [ // repeater matrix! > rename to categories 'title#name', // rename title field to name 'entries' => [ // nested repeater matrix! 'title', 'body' ] ], ], ]; if ($input->urlSegment1 === 'json') { header('Content-type: application/json'); echo $page->pageQueryJson($query); exit(); } Module default settings
      The modules settings are public. They can be adjusted, for example:
      $modules->get('PageQueryBuilder')->debug = true; $modules->get('PageQueryBuilder')->defaults = []; // reset all defaults Default queries for fields:
      Some field types come with default selectors, like Pageimages etc. These are the default queries for template names or object types:
      public $defaults = [ 'queries' => [ 'Pageimages' => [ 'basename', 'url', 'httpUrl', 'description', 'ext', 'focus', ], 'Pagefiles' => [ 'basename', 'url', 'httpUrl', 'description', 'ext', 'filesize', 'filesizeStr', 'hash', ], 'MapMarker' => [ 'lat', 'lng', 'zoom', 'address', ], 'User' => [ 'name', 'email', ], ], ]; These will only be used if there is no nested query for these types. So if you query a field with complex data and do not provide a sub-query, it will be transformed acordingly:
      $page->pageQueryArry(['images']); // returns something like this 'images' => [ 'basename', 'url', 'httpUrl', 'description', 'ext', 'focus'=> [ 'top', 'left', 'zoom', 'default', 'str', ] ]; You can always provide your own sub-query so the defaults will not be used:
      $page->pageQueryArry([ 'images' => [ 'filename', 'description' ], ]); You can also override these, for example:
      $modules->get('PageQueryBuilder')->defaults['queries']['Pageimages'] = [ 'basename', 'url', 'description', ]; Index of nested elements
      The index for nested elements can be adjusted. This is also done with defaults. There are 3 possibilities:
      Nested by name (default) Nested by ID Nested by nummerical indey Named index (default):
      This is the default. If you have a field that contains subpages, their key will be their name:
      // example $pagesByName = [ 'page-1-name' => [ 'title' => "Page one title", 'name' => 'page-1-name', ], 'page-2-name' => [ 'title' => "Page two title", 'name' => 'page-2-name', ] ] ID based index:
      If an object is listed in $defaults['index-id'] their index will be their id. Currently, no items are listed as defautls:
      $modules->get('PageQueryBuilder')->defaults['index-id']['Page']; // example $pagesById = [ 123 => [ 'title' => "Page one title", 'name' => 123, ], 124 => [ 'title' => "Page two title", 'name' => 124, ] ] Number based index
      By default, a couple of fields are transformed automatically to contain numbered indexes:
      // objects or template names that should use numerical indexes for children instead of names $defaults['index-n'] => [ 'Pageimage', 'Pagefile', 'RepeaterMatrixPage', ]; // example $images = [ 0 => [ 'filename' => "image1.jpg", ], 1 => [ 'filename' => "image2.jpg", ] ] When you remove the key 'Pageimage' from $defaults['index-n'], the index will again be name-based.
      Debug
      The module respects wire('config')->debug. It integrates with TracyDebug. You can override it like so:
      // turns on debug output no mather what: $modules->get('PageQueryBuilder')->debug = true; Todos
      Make defualts configurable via Backend. How could that be done in style with the default queries?
    • By daniels
      General
      This is a lightweight alternative to other newsletter & newsletter-subscription modules.
      It can subscribe, update, unsubscribe & delete a user in a list in Mailchimp with MailChimp API 3.0. It does not provide any forms or validation, so you can feel free to use your own. To protect your users, it does not save any user data in logs or sends them to an admin.
      This module fits your needs if you...
      ...use Mailchimp as your newsletter / email-automation tool ...want to let users subscribe to your newsletter on your website ...want to use your own form, validation and messages (with or without the wire forms) ...don't want any personal user data saved in any way in your ProcessWire environment (cf. EU data regulation terms) ...like to subscribe, update, unsubscribe or delete users to/from different lists ...like the Mailchimp UI for creating / sending / reviewing email campaigns You can find it here: https://github.com/danielstieber/SubscribeToMailchimp
      Let me know what you think and if I should add it to the Modules Directory.
      Setup
      Log into your Mailchimp account and go to  Profile > Extras > API Keys. If you don't have an API Key, create a new one. Copy your API Key and paste it in the module settings (Processwire > Modules > Site > SubscribeToMailchimp). Back in Mailchimp, go to the list, where you want your new subscribers. Go to Settings > List name and defaults. Copy the List ID an paste it in to the module settings.
      Usage
      To use the module, you need to load it into your template:
      $mc = $modules->get("SubscribeToMailchimp"); Now you can pass an email address to the module and it will try to edit (if the user exists) or create a new subscriber in your list.
      $mc->subscribe('john.doe@example.com'); You can also pass a data array, to add additional info.
      $mc->subscribe('john.doe@example.com', ['FNAME' => 'John', 'LNAME' => 'Doe']); You can even choose an alternative list, if you don't want this subscriber in your default list.
      $mc->subscribe('john.doe@example.com', ['FNAME' => 'John', 'LNAME' => 'Doe'], 'abcdef1356'); // Subscribe to List ID abcdef1356 If you want to unsubscribe a user from a list, you can use the unsubscribe method.
      $mc->unsubscribe('john.doe@example.com'); // Unsubscribe john.doe@example.com from the default list $mc->unsubscribe('john.doe@example.com', 'abcdef1356'); // Unsubscribe john.doe@example.com from the list abcdef1356 If you want to permantly delete a user, you can call the delete method. Carefully, this step cannot be undone
      $mc->delete('john.doe@example.com'); // Permanently deletes john.doe@example.com from the default list $mc->delete('john.doe@example.com', 'abcdef1356'); // Permanently deletes john.doe@example.com from the list abcdef1356  
      Important Notes
      This module does not do any data validation. Use a sever-sided validation like Valitron Make sure that you have set up your fields in your Mailchimp list. You can do it at Settings > List fields and *|MERGE|* tags Example
      Example usage after a form is submitted on your page:
      // ... validation of form data $mc = $modules->get("SubscribeToMailchimp"); $email = $input->post->email; $subscriber = [ 'FNAME' => $input->post->firstname, 'LNAME' => $input->post->lastname, ]; $mc->subscribe($email, $subscriber);  
      Troubleshooting
      In case of trouble check your ProcessWire warning logs.
      I can't see the subscriber in the list
      If you have enabled double opt-in (it is enabled by default) you will not see the subscriber, until he confirmed the subscription in the email sent by Mailchimp
      I get an error in my ProccessWire warning logs
      Check if you have the right List ID and API Key. Check if you pass fields, that exist in your list. Check if you pass a valid email address. Go to Mailchimps Error Glossary for more Information
      How To Install
      Download the zip file at Github or clone directly the repo into your site/modules If you downloaded the zip file, extract it in your sites/modules directory. You might have to change the folders name to 'SubscribeToMailchimp'. Goto the modules admin page, click on refresh and install it  
      Changelog
      0.0.2
      Note: You can update safely from 0.0.1 without any changes in your code
      New Features
      Added 'Unsubscribe' method $mc->unsubscribe($email, $list = "") Added 'Delete' method $mc->delete($email, $list = "") Bug Fixes and compatibility changes
      Removed type declarations to be compatible with PHP 5.1+* (thanks to wbmnfktr) Other
      Changed the way, the base url for the api gets called *I have only tested it with PHP 7.x so far, so use on owners risk
    • By BitPoet
      As threatened in the Pub sub forum in the "What are you currently building?" thread, I've toyed around with Collabora CODE and built file editing capabilities for office documents (Libre-/OpenOffice formats and MS Office as well as a few really old file types) into a PW module.
      If you are running OwnCloud or NextCloud, you'll perhaps be familiar with the Collabora app for this purpose.
      LoolEditor
      Edit office files directly in ProcessWire
      Edit your docx, odt, pptx, xlsx or whatever office files you have stored in your file fields directly from ProcessWire's page editor. Upload, click the edit icon, make your changes and save. Can be enabled per field, even in template context.
      Currently supports opening and saving of office documents. Locking functionality is in development.
      See the README on GitHub for installation instructions. You should be reasonably experienced with configuring HTTPS and running docker images to get things set up quickly.
      Pull requests are welcome!
      Here is a short demonstration:

    • By Robin S
      An Images field allows you to:
      Rename images by clicking the filename in the edit panel or in list view. Replace images, keeping metadata and filename (when possible) by dropping a new image on the thumbnail in the edit panel. Introduced here. But neither of these things is possible in File fields, which prompted this module. The way that files are renamed or replaced in this module is not as slick as in the Images field but it gets the job done. The most time-consuming part was dealing with the UI differences of the core admin themes. @tpr, gives me even more respect for the work that must go into AdminOnSteroids.
      Most of the code to support the rename/replace features is already present in InputfieldFile - there is just no UI for it currently. So hopefully that means these features will be offered in the core soon and this module can become obsolete.
       
      Files Rename Replace
      Allows files to be renamed or replaced in Page Edit.

      Usage
      Install the Files Rename Replace module.
      If you want to limit the module to certain roles only, select the roles in the module config. If no roles are selected then any role may rename/replace files.
      In Page Edit, click "Rename/Replace" for a file...
      Rename
      Use the text input to edit the existing name (excluding file extension).
      Replace
      Use the "Replace with" select to choose a replacement file from the same field. On page save the file will be replaced with the file you selected. Metadata (description, tags) will be retained, and the filename also if the file extensions are the same.
      Tip: newly uploaded files will appear in the "Replace with" select after the page has been saved.
       
      https://github.com/Toutouwai/FilesRenameReplace
      http://modules.processwire.com/modules/files-rename-replace/
    • By d'Hinnisdaël
      So I decided to wade into module development and created a wrapper module around ImageOptim, a service that compresses and optimizes images in the cloud. ImageOptim currently handles JPG, PNG and GIF files and, depending on the settings you use, shaves off between 15% and 60% in filesize. Great for bandwidth and great for users, especially on mobile.
      This module handles the part of uploading images to ImageOptim via their official API, downloading the optimized version and storing it alongside the original image.
       

       
      Download & Info
      GitHub / Module directory / Readme / Usage
       
      Why ImageOptim?
      There are other image optimization services out there, some of them free, that have outstanding ProcessWire modules. A few things make ImageOptim the best tool for most of my customers: It's not free, i.e. it will probably be around for a while and offers support. However, it's cheaper than some of the bigger competitors like Cloudinary. And it does PNG compression better than any of the free services out there, especially those with alpha channels.
       
      Installation
      Install the module like any other ProcessWire module, by either copying the folder into your modules folder or installing it via the admin. See above for downloads links on GitHub and in the module directory.
       
      Requirements
      To be able to upload images to the service, allow_url_fopen must be set on the server. The module will abort installation if that's not the case.
      I have only tested the module on ProcessWire 3.x installations. I don't see why it shouldn't work in 2.x, if anyone wants to try it out and report back.
       
      ImageOptim account
      To compress images, you first need to sign up for an ImageOptim account. They offer free trials to try the service.
       
      Usage (manual optimization)
      Images can be optimized by calling the optimize() method on any image. You can pass an options array to set ImageOptim API parameters.
      $image->size(800,600)->optimize()->url $image->optimize(['quality' => 'low', 'dpr' => 2]) // Set quality to low and enable hi-dpi mode
      Automatic optimization
      The module also has an automatic mode that optimizes all image variations after resizing. This is the recommended way to use this module since it leaves the original image uncompressed, but optimizes all derivative images.
      $image->size(800,600)->url // nothing to do here; image is optimized automatically
      To change compression setting for single images, you can pass an options array along with the standard ImageResizer options. Passing false disables optimization.
      $image->size(800, 600, ['optimize' => 'medium']) $image->size(800, 600, ['optimize' => ['quality' => 'low', 'dpr' => 2]]) $image->size(800, 600, ['optimize' => false])
      For detailed usage instructions and all API parameters, see the usage instructions on GitHub.
       
      Filenames
      Optimized images will be suffixed, e.g. image.jpg becomes image.optim.jpg. You can configure the suffix in the module settings.
       
      Roadmap
      Asynchronous processing. Not really high on the list. Image variations need to be created anyway, so waiting a few seconds longer on first load is preferable to adding complexity to achieve async optimization. Optimize image variations created by other modules. CroppableImage comes to mind. I don't use any of these, so if somebody wants to help out and submit a pull request — all for it! Add a dedicated page in the setup menu with a dashboard and detailed statistics. ImageOptim's API is very barebones for now, so not sure if that's feasible or even necessary.
      Stability
      I've been using this module on production sites for some time now, without hiccups. If you do notice oddities, feel free to comment here or investigate and submit PRs.