Test event with show attendees

Thursday July 25, 2024 4:39 PM

RSVP deadline is past

Web Development

Creating Wrappers, Sidebars, Callouts and Other Formatting Flourishes with WordPress Gutenberg

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 —

Team Members block from the Editor Blocks collection

— 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.

The Wrapper Block from Editor Blocks sets the background image for the Pricing Table from the same collection.
Keynote Public Speaking Social Collaboration Social Collaboration for Dummies Social Media Team Messaging Video

“Unified or Fragmented” Keynote at Digital Collaboration 2017 in Stockholm

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.


How to Post a Blog-Style Article on LinkedIn

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:

LinkedIn article link
Link to “write an article”

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.

LinkedIn status post

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).

LinkedIn article editor

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:

LinkedIn image and 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.

A notification of an article posted by someone in my network

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

A recent LinkedIn post and other activity displayed on my profile


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.

Web Development

The Professional Power of Public Speaking (webinar replay)

Here is a replay of my YouTube Live event with Donald Kelly, Toastmasters District 47 pubic speaking champion, sponsored by PwC Digital.

Web Development

Fixing WordPress Paragraph Spacing (When the Visual Editor Misleads You)

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.

Post with excess divs
Post with excess divs

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.

Properly formatted post in the WordPress editor.
Properly formatted post in the WordPress editor.
Presentations Public Speaking Toastmasters Video

Speech Video: The Internet of Things

David F. Carr: “The Internet of Things”

A public speaking sample from Club Awesome Toastmasters

Web Development

Notifier for Glip, a new WordPress plugin

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.
WordPress new blog post and comment activity, as recorded in the stream of comments in a Glip team conversation.
WordPress new blog post and comment activity, as recorded in the stream of comments in a Glip team conversation.

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.

Glip WebHooks integration screen
Glip WebHooks integration screen

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:

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'); 
		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


	$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 );


function glip_comment_inserted($comment_id, $comment_object) {

	$webhook_url = get_option('glip_webhook'); 
		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
	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>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>


// 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');
Web Development

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.

API demo
Once a redbooth user authorizes account access, this app displays a basic user directory with the option of adding notes to each user record.

The preliminary setup starts in initialize.php, which sends a request to redbooth.com to start the OAuth authentication process:



$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);


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.



<!doctype html>
<meta charset="utf-8">
<title>User Directory Demo</title>
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;

<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>

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
// 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
// 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
// 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

// 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>
<strong>First Name</strong>: %s<br />
<strong>Last Name</strong>: %s<br />
<strong>Email</strong>: %s
<textarea class=\"status\" id=\"get_%s\" subject_username=\"%s\" me_username=\"%s\"></textarea>
<div id=\"postresult_%s\" ></div>
",$user->avatar_url, $user->first_name, $user->last_name, $user->email, $user->username, $user->username, $me->username, $user->username, $stat[$user->username]);

foreach ($user_directory as $profile) 
	echo $profile;
<script src="https://code.jquery.com/jquery-1.10.2.js"></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( ) {


	$('#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 );

The db.php file is a standard database initialization using the PDO class.

$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
    //echo "Connected successfully"; 
catch(PDOException $e)
    echo "Database connection failed: " . $e->getMessage();

The database access a simple table with this structure.

  `username` varchar(255) NOT NULL,
  `by` varchar(255) NOT NULL,
  `text` text NOT NULL,
  PRIMARY KEY (`id`)

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.

include "db.php";
		$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);
			printf('<p>%s <br /><em>added by %s %s</em></p>',$message,$by,date('F j, Y'));
			echo "<p>Error adding note</p>";

Leica Cameras, Leitz and Zeiss Lenses, etc.

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.


5cm 1:2 Serial #1587970
5cm 1:2 Serial #1587970


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

3.5cm 135 Serial#1290163
3.5cm 135 Serial#1290163 (from 1955)
LEITZ ELMARIT 90mm f/2.8 from 1962 Serial #1760690
LEITZ ELMARIT 90mm f/2.8 from 1962 Serial #1760690
135mm 1:4 Serial #1825635
Leitz Elmar 135mm 1:4 Serial #1825635


This may have collectors value. Serial #196867 10.5 cm 1:63

I believe this is an Alpine Elmar from the 1930s

serial # matches models manufactured in 1934, according to this reference http://www.kenrockwell.com/leica/lens-serial-numbers.htm

Leitz 5cm  1:2 Serial #380257 from 1937
Leitz 5cm 1:2 Serial #380257 from 1937


Leitz 3.5cm 1:3.5 Serial #1519731
Leitz 3.5cm 1:3.5 Serial #1519731 (wide angle lens, mfg 1957, purchased 1961)


Leitz 50mm? 1:4.5 Focotar (don't see serial #)
Leitz 50mm? 1:4.5 Focotar (don’t see serial #)
M3 #1
M3 #1 – based on serial #, this was manufactured in 1959

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

M3 #2 with case
M3 #2 – based on serial #, this is from 1955
M3 #3
M3 #3 – based on serial #, from a batch manufactured 10/11/61
Leica IIf, paperwork says 1955

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

Verascope movie camera
Verascope stereo camera

See http://camera-wiki.org/wiki/Richard_%28Jules%29 or

Bolex 8mm movie camera
Bolex 8mm movie camera – model B8, 1953

Reference http://www.ebay.com/gds/Top-7-Bolex-8mm-Vintage-Movie-Cameras-/10000000178130396/g.html