RSVP deadline is past
Event Listings
As the author of several WordPress plugins, I initially encountered the new Gutenberg editor that became standard in WordPress 5.0 as a disruptive change I had to scramble to adapt to. Since then, I have grown to appreciate it for significantly expanding what you can do with WordPress, particularly in terms of creating custom content formats to support your own uses of the platform.
The new editor model is organized around blocks of content: paragraphs, headings, bullet lists, images, and embedded videos are all blocks, and the editor displays different formatting options depending on the content type. It’s also possible for blocks to be defined with InnerBlocks, meaning that you are allowed to embed other blocks inside them. For example, the new Columns block lets you format content in two or more columns of equal width, and each column can include multiple paragraphs, images, and so on.
In addition, blocks can be programmed to act like WordPress shortcodes — placeholders for dynamic content that requires server-side processing. I’ve used that technique in the event listings block for my RSVPMaker plugin. In this post, though, I’m focusing on formatting of ordinary content.
Some parts of the new WordPress editing experience still need improvement, but I’m encouraged by the pace at which enhancements to the core platform have been arriving. If the content formatting controls delivered as part of the base platform don’t quite meet your needs, there are a few ways you can achieve the effects you desire:
- Take advantage of plugins that provide a libraries of additional formatting blocks, enhancing both how you work in the editor and what visitors see on your site.
- Take advantage of the Additional CSS Class field you will find in the Advanced tab of every block to tag a specific block of content. Use the Customize utility’s Additional CSS tab to provide additional formatting commands targeting that content.
- Create your own Gutenberg plugins, as I’ve done with Floating Callout and My Marginalia, to meet your own needs or those of clients.
I share examples of each of these below.
Gutenberg Block Collections
Although I’ve created plugins that enable a single custom block to address a specific need, some talented interactive designers have created collections of components to meet common requirements. One of the best I’ve found so far is Editor Blocks by Danny Cooper, which includes some nice components for formatting team member or product description tables —

— plus a nice wrapper or container block that allows you to specify margins, padding, and a background color or image to wrap around any content, including one of their other components.

This speech updates themes from my Social Collaboration for Dummies book (Wiley, 2013). Since the book was published, team messaging tools such as Slack have begun to generate much more excitement, investment, and competitive energy than the enterprise social networking platforms I was writing about at that time.
At IDG’s Digital Collaboration 2017 conference in Stockholm, I discussed this trend in the context of efforts to unify multiple modes of communication and collaboration in one platform versus the trend toward rapid proliferation of specialized workplace tools — and the opportunities to create unified experiences out of diverse tools.
I believe in the value of having a blog built into a business website, but there is also undeniable value in taking advantage of the publishing opportunities built into a couple of social networking sites: LinkedIn and Medium.
When you blog on your own site, you are enriching your site with content, giving visitors more insight into your business and business philosophy, and boosting your site’s search engine optimization score. In other words, you are trying to bring people to you and give them something to read when they come to you.
When you post on LinkedIn or Medium, you are going where the people are — or at least a rich concentration of people you might want to reach. These two approaches can work together, where you post something on LinkedIn or Medium that links back to your site’s blog or other content on your own site.
LinkedIn is particularly interesting for reaching an audience of people who are trying to get a new job or advance in their profession. I wrote this post partly as a guide for a friend who is starting a professional coaching business.
Because LinkedIn has been around since 2002 and most people know it as the social network stocked with interactive resumes and people sending “I would like to add you to my professional network” messages to each other, not everyone knows that it now essentially contains a blogging platform that is available to them.
As of the redesign that arrived in late 2016, here is where you find the blogging function at the top of the LinkedIn home screen:

You still have the option of writing a quick status post, which can optionally include a link, a photo, and one or more references to other users (type the @ symbol followed by the first few letters of the person’s name to get LinkedIn to search your contacts). This is useful for sharing something that would be interesting to people in your network, without taking the time to write an essay about it.

When you click “write an article,” you instead get taken to a full screen editor that allows you to write a headline and the body of a post, with typical word processing controls for marking something bold or italic or adding bullets or numbered lists. If you copy and paste from Microsoft Word or another word processor, the formatting should translate fairly well (check it over carefully for any formatting or alignment glitches you might need to correct).
LinkedIn encourages you to add a large feature photo or image to be associated with your post, which you do by clicking the region at the top of the editor (the gray box with the + sign and image icons).

You can also add photos, video, and other media in the body of a post.
Much like the WordPress editor, the LinkedIn editor makes it relatively simple to add embedded media such as YouTube videos. Just paste in the url (web address) for an individual video on a blank line, and the video player will be added automatically.
Or you can click on this little icon in the left margin to display this row of media insert controls:

When you publish one of these articles, it is highlighted more prominently in the LinkedIn notifications scheme than if you had published a simple status post. A new article from someone in your network shows up in the same stream of notifications where LinkedIn shows that someone else has interacted with your profile or your content.

Your latest LinkedIn post is also prominently displayed on your profile, along with your other most recent activity on the network.

One thing to be careful about is posting the identical content multiple places. For SEO, it’s better to drive all traffic to a single version of an article at a single web address. However, if you publish something on LinkedIn that you would also like to share on your own blog, you can do so without the SEO penalty if you remember to set the LinkedIn version as the canonical version of the article. See this explanation from the creators of the Yoast SEO plugin for WordPress.
Like all of social media, the LinkedIn publishing capability is something to explored in a spirit of experimentation. Find out what works for you and draws a positive reaction from your network.
Here is a replay of my YouTube Live event with Donald Kelly, Toastmasters District 47 pubic speaking champion, sponsored by PwC Digital.
A client of mine who blogs with WordPress ran into an issue with new posts appearing on her site without the proper spacing between paragraphs. I’m sharing the solution in case others might find it useful.
Everything looked fine in the WordPress visual editor, but these posts looked wrong on the public website. I thought at first it was problem with the site theme / CSS, but older posts on the same site were displaying fine. The only way to figure out what was going wrong was to view these posts with the editor toggled from Visual to Text to reveal some code that shouldn’t have been there. Every paragraph was tagged as a div — which in HTML is a block of text with no default styling. Also, an empty div is not displayed at all, so the blank lines she thought she had in her copy disappeared on the public site.
I suspect the divs got in there because this content had been copied and pasted from some other source, probably some other web-based word processor or blog posting system. WordPress usually does a pretty good job of handling content posted in from Microsoft Word, but not necessarily other sources.

In visual mode, WordPress identified all these divs as paragraphs, by the way, which was not helpful. Once you have paragraphs tagged as divs, WordPress keeps tagging any additional content you insert the same way.
To fix the posts, I had to manually remove the <div> and </div> tags. What a WordPress post normally looks like in the editor is a mix of text and HTML (for links and images) with a blank line between paragraphs. WordPress normally handles these formatting chores automatically — it just doesn’t handle an attack of the divs very well.

This plugin will post a notification to a Glip team conversation whenever a new blog article is posted or a comment is posted to a blog that you maintain. The plugin uses the WebHooks interface to connect with Glip, the team collaboration and productivity platform from RingCentral.
The plugin is available through the WordPress.org plugins repository: https://wordpress.org/plugins/notifier-for-glip/
I do editorial consulting for Glip, so this came about because I was trying to cook up an example of using their support for WebHooks (a standard web services API). Commented code included below.
Target audience for this plugin:
- Anyone who runs a WordPress website with multiple contributors and people posting comments.
- Web developers and designers who need to keep tabs on a client’s blog posts and comment activity.

How It Works
The WordPress admin must set the WebHooks url for Glip on the Settings -> Glip screen.
Here is where you obtain that information from within Glip, after activating the WebHooks integration.

The plugin uses WordPress action hooks to detect new posts and comments and relay them to Glip in the JSON format specified above.
Here is the code I used for the initial release:
<?php /* Plugin Name: Notifier for Glip Plugin URI: http://www.carrcommunications.com/notifier-for-glip/ Description: Post a notification to a Glip team conversation whenever a new blog article is posted or a comment is posted to a blog that you maintain. The plugin uses the WebHooks interface to connect with Glip, the team collaboration and productivity platform from RingCentral. Author: David F. Carr Version: 0.9 Author URI: http://www.carrcommunications.com */ function glip_webhook($webhook_url,$title, $body, $activity,$icon ='' ) { $json = json_encode(array('icon'=>$icon,'activity'=>$activity,'title'=>$title,'body'=>$body) ); $ch = curl_init($webhook_url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_POSTFIELDS, $json); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json', 'Content-Length: ' . strlen($json)) ); $r = curl_exec($ch); return $r; } function glip_post_published_notification( $ID, $post, $update ) { if ($post->post_date != $post->post_modified) return; // don't do this for edits to a previously published post $webhook_url = get_option('glip_webhook'); if(empty($webhook_url)) return; // won't work without it $author = $post->post_author; /* Post author ID. */ $name = get_the_author_meta( 'display_name', $author ); $email = get_the_author_meta( 'user_email', $author ); $title = $post->post_title; $permalink = get_permalink( $ID ); $edit = get_edit_post_link( $ID, '' ); $start = substr(strip_tags($post->post_content),0,100); $title = $update.sprintf('New post to blog: [%s](%s) by %s %s',$title, $permalink,$name,$email); $body = sprintf('%s %s [Edit](%s)',$_SERVER['SERVER_NAME'],$start,$edit); $icon = plugins_url('wordpress-logo-32-blue.png',__FILE__); $activity = 'New Blog Post'; glip_webhook($webhook_url,$title, $body, $activity,$icon); } add_action( 'publish_post', 'glip_post_published_notification', 10, 2 ); add_action('wp_insert_comment','glip_comment_inserted',99,2); function glip_comment_inserted($comment_id, $comment_object) { $webhook_url = get_option('glip_webhook'); if(empty($webhook_url)) return; // won't work without it $permalink = get_permalink($comment_object->comment_post_ID); $title = sprintf('New comment on blog by %s %s',$comment_object->comment_author, $comment_object->comment_author_email); $body = sprintf('%s %s Post: [%s](%s)',$_SERVER['SERVER_NAME'],$comment_object->comment_content,get_the_title($comment_object->comment_post_ID),$permalink); $icon = plugins_url('wordpress-logo-32-blue.png',__FILE__); $activity = 'New Comment on Blog'; glip_webhook($webhook_url,$title, $body, $activity,$icon); } add_action('admin_init', 'glip_options_init' ); add_action('admin_menu', 'glip_options_add_page'); // Init plugin options to white list our options function glip_options_init(){ register_setting( 'glip_webhook_options', 'glip_webhook', 'glip_options_validate' ); } // Add menu page function glip_options_add_page() { add_options_page('Glip', 'Glip', 'manage_options', 'glip_options', 'glip_options_do_page'); } // Draw the menu page itself function glip_options_do_page() { ?> <div class="wrap"> <h2>Glip Options</h2> <form method="post" action="options.php"> <?php settings_fields('glip_webhook_options'); ?> <?php $webhook = get_option('glip_webhook'); ?> WebHooks address: <input name="glip_webhook" id="glip_webhook"value="<?php echo $webhook?>" /> <p class="submit"> <input type="submit" class="button-primary" value="<?php _e('Save Changes') ?>" /> </p> </form> <p>You will find this web address in the settings screen for the WebHooks integration, which is included with every Glip account. A menu in the upper right hand corner allows you to change the team conversation updates will be posted to.</p> <p><img src="<?php echo plugins_url('glip-webhooks.png',__FILE__); ?>" width="580" height="372" alt="Webhooks" /></p> </div> <?php } // Sanitize and validate input. function glip_options_validate($input) { if (!filter_var($input, FILTER_VALIDATE_URL) === false) { return $input; } else { return ''; } } function glip_admin_notice () { $w = get_option('glip_webhook'); if(empty ($w) ) printf('<div class="error">%s <a href="%s">%s</a></div>',__('WebHooks URL must be set for ','glipnotifier'),admin_url('options-general.php?page=glip_options'), __('Glip integration','glipnotifier')); } add_action('admin_notices', 'glip_admin_notice'); ?>
My First Redbooth API Demo
Shortly after joining Redbooth as an employee, I began playing with the collaboration platform’s application user interface. As a writer who also writes code, I was curious to see whether I could come up with something useful.
The Redbooth API makes use of the OAuth2 authentication method, which provides a standard way for a user of one application to give permission for another application to access protected resources. For example, once the test is past, an external application that has received the user’s blessing can retrieve access files stored in a Redbooth workspace or add tasks to a task list. The application retrieves a cryptographic authentication token from Redbooth, which it then uses as proof of its right to access these resources.
One of the first ideas I had was to display some sort of interactive user directory. I can imagine a few different scenarios where it might be handy to store some supplemental information about employee performance or vacation schedules in your own app, using the data structure you define rather than one provided within Redbooth.
This version shown below (which you can access live here) pulls the names and profile photos for all the other users in your organization and allows you to add notes to each of these mini-profiles.
Note that you’re welcome to use this application for real, as long as you’re willing to do so at your own risk; it’s not running on Redbooth’s secure infrastructure, and the data you add goes into a MySQL database on my server. If you have a serious application for something like this, you could use the code shown below as a starting point (if you’re working in PHP) or as inspiration for an app you might implement in the language of your choice, on your own server.

The preliminary setup starts in initialize.php, which sends a request to redbooth.com to start the OAuth authentication process:
initialize.php
<?php $apikey = 'KEY_GOES_HERE'; // make sure to url encode the redirect URL $q = urlencode('http://tabmgr.com/rb/auth.php'); //provide the url for auth.php on your server // construct the authorization query with our apikey and the redirect URL $endpoint = 'https://redbooth.com/oauth2/authorize?client_id=' . $apikey . '&redirect_uri=' . $q . '&response_type=code'; //redirect to authorization URL header("Location: " . $endpoint); exit; ?>
The main workhorse functions are in auth.php. In the previous step, we queried redbooth.com for a code to be used to kick off the authentication process. The code is returned as a GET query parameter appended to the redirect uri specified in initialize.php. The API key and API secret are used to verify that the user has given permission for access to his or her redbooth.com account. The remainder of the code in this script is devoted to sorting the user records by last name, formatting them for display, and setting up a JQuery/AJAX routine to post notes about each user to a local database.
Any existing notes are retrieved as part of a database routine that runs right before we start iterating through the user records retrieved from redbooth.com.
auth.php
<?php session_start(); ?> <!doctype html> <html> <head> <meta charset="utf-8"> <title>User Directory Demo</title> <style> textarea { width: 600px; border: thin solid blue; height: 2em; } body { background-color: #CC0000; } #content { width: 700px; padding-left: 15px; padding-right: 15px; margin-left: auto; margin-right: auto; background-color: #eee; } </style> </head> <body> <div id="content"> <h1>Dave's unofficial API demo</h1> <p>Once authorized, the app pulls a list of the other users in your organization from Redbooth. You can add a note to any user record by typing it into the blank and pressing enter.</p> <?php function rb_authorize () { $code = $_GET["code"]; $apikey = 'KEY_GOES_HERE'; $appsecret = 'SECRET_GOES_HERE'; // make sure to url encode the return URL $q = urlencode('http://tabmgr.com/rb/auth.php'); //ex: http://www.mytest.com/auth.php // construct the authorization query with our apikey and the returned code to get the access_token $endpoint = 'https://redbooth.com/oauth2/token?client_id=' . $apikey . '&client_secret=' . $appsecret . '&code=' . $code . '&grant_type=authorization_code&redirect_uri=' . $q; // setup curl to make a call to the endpoint $session = curl_init($endpoint); // indicates that we want the response back curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // post curl_setopt($session, CURLOPT_POST, true); // exec curl and get the data back $data = curl_exec($session); // remember to close the curl session once we are finished retrieveing the data curl_close($session); // decode the json data to make it easier to parse the php $auth_result = json_decode($data); // check for empty data if ($auth_result === NULL) die('Error parsing json for auth result'); // Get the access_token $_SESSION["access_token"] = $access_token = $auth_result->access_token; return $access_token; } function get_me($access_token) { // construct the query with the access_token to get the user's account details $endpoint = 'https://redbooth.com/api/3/me?access_token=' . $access_token; // setup curl to make a call to the endpoint $session = curl_init($endpoint); // indicates that we want the response back curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // exec curl and get the data back $data = curl_exec($session); // remember to close the curl session once we are finished retrieveing the data curl_close($session); // decode the json data to make it easier to parse the php $me = json_decode($data); return $me; } if($_SESSION["access_token"] && !$_GET["new"]) $access_token = $_SESSION["access_token"]; // todo - check expiration else { // Get the returned code $access_token = rb_authorize (); } $me = get_me($access_token); // check for empty data if ($me === NULL) { // try again to get current user data $access_token = rb_authorize (); $me = get_me($access_token); } if ($me === NULL) die('Error parsing json for /me'); // construct the query with the access_token to get the user's account details $endpoint = 'https://redbooth.com/api/3/users?access_token=' . $access_token; // setup curl to make a call to the endpoint $session = curl_init($endpoint); // indicates that we want the response back curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // exec curl and get the data back $data = curl_exec($session); // remember to close the curl session once we are finished retrieveing the data curl_close($session); // decode the json data to make it easier to parse the php $users = json_decode($data); // check for empty data if ($users === NULL) die('Error parsing json for users listing: '.$data); $count = sizeof($users); echo "<p><em>Fetched $count users</em></p>"; // get any status notes about the users from the database and put them in an array // keyed to the redbooth username include "db.php"; $sql = "SELECT * FROM status ORDER BY timestamp DESC"; foreach ($conn->query($sql) as $row) { $stat[$row['username']] .= '<p>'.$row['text'] . "<br /><em>posted by @" . $row["by"] .' at ' . date('F j, Y',strtotime($row["timestamp"]))."</em><p>\n"; } foreach($users as $user) { $uindex = strtolower(preg_replace('/[^A-Za-z]/','',$user->last_name.$user->first_name.$user->email)); $user_directory[$uindex] = sprintf(" <div style=\"float: right; clear:both\"><img src=\"%s\"></div> <p> <strong>First Name</strong>: %s<br /> <strong>Last Name</strong>: %s<br /> <strong>Email</strong>: %s </p> <p> <textarea class=\"status\" id=\"get_%s\" subject_username=\"%s\" me_username=\"%s\"></textarea> </p> <div id=\"postresult_%s\" ></div> %s ",$user->avatar_url, $user->first_name, $user->last_name, $user->email, $user->username, $user->username, $me->username, $user->username, $stat[$user->username]); } ksort($user_directory); foreach ($user_directory as $profile) echo $profile; ?> </div> <script src="https://code.jquery.com/jquery-1.10.2.js"></script> <script> $( ".status" ) .keypress(function() { if(event.keyCode == 13) { PostRbStatus(this.getAttribute("subject_username"), this.getAttribute("me_username"), this.value) } }) function PostRbStatus(subject_username, me_username, message) { $.post( "/rb/poststatus.php", {subject_username: subject_username, me_username: me_username, message: message}) .done(function( data ) { $( "#postresult_" + subject_username).html( data ) }) .error(function( ) { alert("error"); }); $('#get_' + subject_username).blur(); $('#get_' + subject_username).value(''); } $( "textarea" ) .on( "mouseenter", function() { $( this ).css({ "border": "medium solid #CC0000", "height": "5em" }); }) .on( "mouseleave", function() { var styles = { "border": "thin solid blue", "height": "2em" }; $( this ).css( styles ); }); </script> </body> </html>
The db.php file is a standard database initialization using the PDO class.
<?php $servername = "localhost"; $username = "USERNAME_GOES_HERE"; $password = "PASSWORD_GOES_HERE"; try { $conn = new PDO("mysql:host=$servername;dbname=tabmgr_rb", $username, $password); // set the PDO error mode to exception $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //echo "Connected successfully"; } catch(PDOException $e) { echo "Database connection failed: " . $e->getMessage(); } ?>
The database access a simple table with this structure.
CREATE TABLE IF NOT EXISTS `status` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `by` varchar(255) NOT NULL, `text` text NOT NULL, `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Here is the server-side script that processes the data submitted for notes on each user. The output is captured by the client-side JavaScript and placed in a div above the previous entries in the database.
<?php include "db.php"; if($_POST["subject_username"]) { $username = $_POST["subject_username"]; $by = $_POST["me_username"]; $message = $_POST["message"]; $sql = sprintf("INSERT INTO status (`username`, `by`, `text`) VALUES('%s', '%s', '%s') ", $username, $by, $message); $count = $conn->exec($sql); if($count) printf('<p>%s <br /><em>added by %s %s</em></p>',$message,$by,date('F j, Y')); else echo "<p>Error adding note</p>"; } ?>
I’m currently seeking offers or advice on how to value several vintage cameras, mostly Leicas, as well as lenses and accessories. This collection also includes movie and portrait cameras. Most are from the 1950s and 1960s and some may go back to the 1940s. Full set includes an assortment of lens filters and adapters not shown here. These were handed down from my father and grandfather.
My grandfather was a veteran of World War I, so it’s possible some of these such as the Verascope stereo camera are older than I realize.
If interested, contact david@carrcommunications.com.
Lenses

5cm 1:2 Serial #1318695
(purchased 1956)



This may have collectors value. Serial #196867 10.5 cm 1:63
serial # matches models manufactured in 1934, according to this reference http://www.kenrockwell.com/leica/lens-serial-numbers.htm





M3 dates based on this list https://www.cameraquest.com/mtype.htm





Leica IIf according to this serial # lookup https://www.cameraquest.com/ltmnum.htm

See http://camera-wiki.org/wiki/Richard_%28Jules%29 or
http://licm.org.uk/livingImage/Verascope.html

Reference http://www.ebay.com/gds/Top-7-Bolex-8mm-Vintage-Movie-Cameras-/10000000178130396/g.html
You must be logged in to post a comment.