diff --git a/README.md b/README.md new file mode 100644 index 0000000..2160d55 --- /dev/null +++ b/README.md @@ -0,0 +1,138 @@ +# DocumentCloud WordPress plugin + +The DocumentCloud WordPress plugin lets you embed [DocumentCloud](https://www.documentcloud.org/) resources into WordPress content using [shortcodes](https://codex.wordpress.org/Shortcode_API). + + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html"] + +## Installation + +1. Upload the contents of the plugin to `wp-content/plugins/documentcloud` +2. Activate the plugin through the "Plugins" menu +3. In your posts, embed documents, pages, or notes using the DocumentCloud button or the `[documentcloud]` shortcode +4. Optional: Set a default width/height for all DocumentCloud embeds (which can be overridden on a per-embed basis with the `height/width` attributes) at Settings > DocumentCloud. (This default width will only be used if you set `responsive="false"` on an embed.) + +**Upgrading from Navis DocumentCloud:** If you're currently using the Navis DocumentCloud plugin (from which this plugin was built), you'll want to deactivate or delete it before installing this plugin. + +## Usage + +This plugin allows you to embed DocumentCloud resources using either the raw URL on its own line: + + Here's something you should really take a look at: + + https://www.documentcloud.org/documents/282753-lefler-thesis.html + + Isn't that interesting? + +Or a custom shortcode: + + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html"] + +When you save, WordPress fetches and stores the actual embed code HTML from the DocumentCloud servers using oEmbed. You can freely toggle between visual and HTML mode without mangling embed code, and your embed will always be up to date with the latest embed code. + +By default, documents will have a responsive width (it will narrow and widen as necessary to fill available content area) and use the theme's default height. If you want to override this, you can either set `responsive="false"` or explicitly set a `width`: + + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html" width="600"] + +You can set your own defaults in Settings > DocumentCloud, but default widths will be ignored unless `responsive` is disabled: + + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html" responsive="false"] + +To embed a single page, use any page-specific URL. Pages ignore `width/height` and always act responsively: + + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html#document/p1"] + +To embed a note, use any note-specific URL. Notes ignore `width/height` and always act responsively: + + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html#document/p1/a53674"] + +Here's the full list of embed options you can pass via shortcode attributes; some are specific to the type of resource you're embedding. + +### All resources: + +- `url` (**required**, string): Full URL of the DocumentCloud resource. +- `container` (string): ID of element to insert the embed into; if excluded, embedder will create its own container. + +### Documents only: + +- `height` (integer): Height (in pixels) of the embed. +- `width` (integer): Width (in pixels) of the embed. If used, will implicitly set `responsive="false"`. +- `responsive` (boolean): Use responsive layout, which dynamically adjusts width to fill content area. Defaults `true`. +- `responsive_offset` (integer): Distance (in pixels) to vertically offset the viewer for some responsive embeds. +- `page` (integer): Page number to have the document scroll to by default. +- `note` (integer): ID of the note that the document should highlight by default. +- `notes` (boolean): Hide or show notes. +- `search` (boolean): Hide or show search form. +- `sidebar` (boolean): Hide or show sidebar. +- `pdf` (boolean): Hide or show link to download original PDF. +- `text` (boolean): Hide or show text tab. +- `zoom` (boolean): Hide or show zoom slider. +- `format` (string): Indicate to the theme that this is a wide asset by setting this to `wide`. Defaults `normal`. + +You can read more about publishing and embedding DocumentCloud resources on https://www.documentcloud.org/help/publishing. + +## How the oEmbed endpoint is discovered + +To make a resource [discoverable](http://oembed.com/#section4) by oEmbed consumers, you can include a `` tag that specifies the oEmbed endpoint URL. So in one version of reality, once you tell WordPress "this resource is oEmbeddable", WordPress would cURL the resource URL, look for the oEmbed link tag in its header, pluck out the oEmbed endpoint from the `` tag's `href`, and then hit that endpoint for the embed code. In our actual reality, that's considered a waste of a cURL, so we have to actually describe the format of our oEmbed endpoint within WordPress itself. + +## Caching + +Ideally, when WordPress hits our oEmbed service to fetch the embed code, it would obey the `cache_age` we return. Despite [conversation](https://core.trac.wordpress.org/ticket/14759) around this, it doesn't seem to. + +Instead, it lets us choose between no cache at all (so *every pageload* triggers a call to our oEmbed service to get the embed code) or a supposed 24-hour cache stored in the `postmeta` table. Unfortunately, [our tests](https://github.com/documentcloud/wordpress-documentcloud/issues/20) seem to show this cache is never expired, which means we can choose between no cache (thus possibly DDOSing ourselves) or a permanent cache (thus possibly having stale embed codes). We've chosen the latter; hopefully this cache does eventually expire, and our embed codes shouldn't change that often anyway. + +If you find yourself absolutely needing to expire the cache, though, you have two choices: + +1. Delete the appropriate `_oembed_*` rows from your `postmeta` table. +2. Modify the shortcode attributes for the embed, since this is recognized as a new embed by WordPress. + +## Changelog + +### 0.4.3 +* Separate the oEmbed config options (provided as params to the endpoint) from the embed config options (encoded as params on the `url`) (#48) +* Rename `default_page` and `default_note` options back to `page` and `note` (#47) +* Remove `sidebar`, `text`, and `pdf` default values + +### 0.4.2 +* Recognize Unicode document slugs (#37) + +### 0.4.1 +* Conform syntax to WordPress VIP plugin requirements (#30) (@bcampeau) +* Fixed: Bare URLs now get default parameters (#35) +* Fixed: Pages/notes on docs with uppercase slugs now embeddable (#36) + +### 0.4.0 +* Support embedding pages (#28) +* Support embedding raw contextual page/note URLs (#29) +* Check for existence of things to stop triggering PHP notices (#27) +* Add DocumentCloud logo for plugin (#26) +* Fix ability to specify a container in the shortcode +* Improve embeddable resource pattern recognition + +### 0.3.3 +* Remove unused TinyMCE components +* Resolve CVE-2015-2807 reported by dxw Security at https://security.dxw.com/advisories/publicly-exploitable-xss-in-wordpress-plugin-navis-documentcloud/ + +### 0.3.2 +* Implement a few best practice security measures + +### 0.3.1 +* Check for old (Navis) plugin and warn admins of conflict +* Add note about raw URLs to README +* Stop storing shortcode attributes in the `postmeta` table + +### 0.3 +* Add support for embedding notes. +* Default to responsive. +* Enable caching. + +### 0.2 +* Fetch embed code via oEmbed instead of generating statically. +* Add new options: `container`, `responsive`, `responsive_offset`, `default_page`, `default_note`, `notes`, `search`, and `zoom`. +* Deprecate `id` attribute. It's still usable, but support may drop in the future. Use `url` instead. + +### 0.1 +* Initial release. + +## License and History + +The DocumentCloud WordPress plugin is [GPLv2](http://www.gnu.org/licenses/gpl-2.0.html). Initial development of this plugin by Chris Amico (@eyeseast) supported by [NPR](http://www.npr.org) as part of the [StateImpact](http://stateimpact.npr.org) project. Development continued by Justin Reese (@reefdog) at [DocumentCloud](https://www.documentcloud.org/). diff --git a/assets/icon-128x128.png b/assets/icon-128x128.png new file mode 100644 index 0000000..8497905 Binary files /dev/null and b/assets/icon-128x128.png differ diff --git a/assets/icon-256x256.png b/assets/icon-256x256.png new file mode 100644 index 0000000..37352a6 Binary files /dev/null and b/assets/icon-256x256.png differ diff --git a/documentcloud.php b/documentcloud.php new file mode 100644 index 0000000..538a539 --- /dev/null +++ b/documentcloud.php @@ -0,0 +1,423 @@ +https?):\/\/www\.documentcloud\.org\/documents\/(?P[0-9]+-[\p{L}\p{N}%-]+)'; + /** + * Constructor. + */ + function __construct() { + // Check for conflicts with other DocumentCloud plugins. + // Not needed on WordPress VIP since no other DocumentCloud plugins exist. + if ( ! defined( 'WPCOM_IS_VIP_ENV' ) || ! WPCOM_IS_VIP_ENV ) { + add_action( 'admin_init', array( $this, 'check_dc_plugin_conflict' ) ); + } + + // Register the oEmbed provider + add_action( 'init', array( $this, 'register_dc_oembed_provider' ) ); + + // Set the textdomain for the plugin so it is translation compatible + add_action( 'plugins_loaded', array( $this, 'load_plugin_textdomain' ) ); + + // Only called when `[documentcloud]` shortcode is used + add_shortcode( 'documentcloud', array( $this, 'process_dc_shortcode' ) ); + + // Called just before oEmbed endpoint is hit + add_filter( 'oembed_fetch_url', array( $this, 'prepare_oembed_fetch' ), 10, 3 ); + + // Setup the settings page + add_action( 'admin_menu', array( $this, 'add_options_page' ) ); + add_action( 'admin_init', array( $this, 'settings_init' ) ); + } + + /** + * Load plugin textdomain. + */ + function load_plugin_textdomain() { + load_plugin_textdomain( 'documentcloud' ); + } + + /** + * Check for conflicts with the Navis DocumentCloud plugin. + */ + function check_dc_plugin_conflict() { + if ( is_plugin_active( 'navis-documentcloud/navis-documentcloud.php' ) ) { + add_action( 'admin_notices', array( $this, 'dc_conflict_admin_notice' ) ); + } + } + + /** + * Create an admin notice when conflicts exist with Navis DocumentCloud. + */ + function dc_conflict_admin_notice() { + ?> +
+

Warning! You have two conflicting DocumentCloud plugins activated. Please deactivate Navis DocumentCloud, which has been replaced by DocumentCloud.', 'documentcloud' ) ); ?>

+
+ $height, + 'width' => $width, + 'full_width' => $full_width, + ); + } + + /** + * Get the attribute defaults for the shortcode. + * + * @return array + */ + function get_default_atts() { + $default_sizes = $this->get_default_sizes(); + + return array( + 'url' => null, + 'container' => null, + 'notes' => null, + 'responsive_offset' => null, + 'page' => null, + 'note' => null, + 'zoom' => null, + 'search' => null, + 'responsive' => null, + 'sidebar' => null, + 'text' => null, + 'pdf' => null, + // The following defaults match the existing plugin, except + // `height/width` are prefixed `max*` per the oEmbed spec. + // You can still use `height/width` for backwards + // compatibility, but they'll be mapped to `max*`. + // Precedence (lower number == higher priority): + // 1. `width` on shortcode + // 2. `maxwidth` on shortcode + // 3. Settings > DocumentCloud > "Default embed width" + // 4. `wp_embed_defaults()['width']` + 'maxheight' => $default_sizes['height'], + 'maxwidth' => $default_sizes['width'], + 'format' => 'normal', + ); + } + + /** + * Prepare the oEmbed fetch URL. + * + * @param string $provider + * @param string $url + * @param array $args + * @return string + */ + function prepare_oembed_fetch( $provider, $url, $args ) { + // Merge actual args with default attributes so that defaults are always + // sent to oEmbed endpoint + $default_atts = $this->get_default_atts(); + $atts = array_merge( $default_atts, $args ); + + // Some resources (like notes) have multiple possible + // user-facing URLs. We recompose them into a single form. + $url = $this->clean_dc_url( $url ); + + // Send these to the oEmbed endpoint itself + $oembed_config_keys = array( 'maxheight', 'maxwidth' ); + + // Specifically *don't* include these on the embed config itself + $excluded_embed_config_keys = array( 'url', 'format', 'height', 'width', 'maxheight', 'maxwidth', 'discover' ); + + // Clean and prepare arguments + foreach ( $atts as $key => $value ) { + if ( in_array( $key, $oembed_config_keys ) ) { + $provider = add_query_arg( $key, $value, $provider ); + } + if ( ! in_array( $key, $excluded_embed_config_keys ) ) { + // Without this check, `add_query_arg()` will treat values + // that are actually ID selectors, like `container=#foo`, + // as URL fragments and throw them at the end of the URL. + if ( 0 === strpos( $value, '#' ) ) { + $value = urlencode( $value ); + } + $url = add_query_arg( $key, $value, $url ); + } + } + + $provider = add_query_arg( 'url', urlencode( $url ), $provider ); + + return $provider; + } + + /** + * Create the DocumentCloud embed output from the shortcode. + * + * @param array $atts + * @return string + */ + function process_dc_shortcode( $atts ) { + $default_sizes = $this->get_default_sizes(); + $default_atts = $this->get_default_atts(); + + // Smooshes together passed-in shortcode attrs with defaults + // and filters to only those we accept. + $filtered_atts = shortcode_atts( $default_atts, $atts ); + + // Either the `url` or `id` attributes are required, but `id` + // is only supported for backwards compatibility. If it's used, + // we force this to embed a document. I.e., `id` can't be used + // for embedding notes, pages, or other non-document resources. + if ( empty( $atts['url'] ) ) { + if ( empty( $atts['id'] ) ) { + return ''; + } else { + $url = $filtered_atts['url'] = 'https://' . WP_DocumentCloud::OEMBED_RESOURCE_DOMAIN . "/documents/{$atts['id']}.html"; + } + } + + // `height/width` beat `maxheight/maxwidth`; see full precedence list in `get_default_atts()`. + if ( isset( $atts['height'] ) ) { + $filtered_atts['maxheight'] = $atts['height']; + } + if ( isset( $atts['width'] ) ) { + $filtered_atts['maxwidth'] = $atts['width']; + } + + // `responsive` defaults true, but our responsive layout + // ignores width declarations. If a user indicates a width and + // hasn't otherwise specifically indicated `responsive='true'`, + // it's safe to assume they expect us to respect the width, so + // we disable the responsive flag. + if ( ( isset( $atts['width'] ) || isset( $atts['maxwidth'] ) ) && 'true' !== $atts['responsive'] ) { + $filtered_atts['responsive'] = 'false'; + } + + // If the format is set to wide, it blows away all other width + // settings. + if ( 'wide' === $filtered_atts['format'] ) { + $filtered_atts['maxwidth'] = $default_sizes['full_width']; + } + + // For the benefit of some templates, notify template that + // we're requesting an asset wider than the default size. + global $post; + $is_wide = intval( $filtered_atts['maxwidth'] ) > $default_sizes['width']; + + if ( apply_filters( 'documentcloud_caching_enabled', WP_DocumentCloud::CACHING_ENABLED ) ) { + // This lets WordPress cache the result of the oEmbed call. + // Thanks to http://bit.ly/1HykA0U for this pattern. + global $wp_embed; + $url = $filtered_atts['url'] = $this->clean_dc_url( $atts['url'] ); + return $wp_embed->shortcode( $filtered_atts, $url ); + } else { + return wp_oembed_get( $atts['url'], $filtered_atts ); + } + + } + + /** + * Parse the DocumentCloud URL into its components. + * + * @param string $url + * @return array + */ + function parse_dc_url( $url ) { + $patterns = array( + // Document + '{' . WP_DocumentCloud::DOCUMENT_PATTERN . '\.html$}', + // Pages and page variants + '{' . WP_DocumentCloud::DOCUMENT_PATTERN . '.html#document\/p(?P[0-9]+)$}', + '{' . WP_DocumentCloud::DOCUMENT_PATTERN . '\/pages\/(?P[0-9]+)\.(html|js)$}', + // Notes and note variants + '{' . WP_DocumentCloud::DOCUMENT_PATTERN . '\/annotations\/(?P[0-9]+)\.(html|js)$}', + '{' . WP_DocumentCloud::DOCUMENT_PATTERN . '.html#document\/p([0-9]+)/a(?P[0-9]+)$}', + '{' . WP_DocumentCloud::DOCUMENT_PATTERN . '.html#annotation\/a(?P[0-9]+)$}', + ); + + $elements = array(); + foreach ( $patterns as $pattern ) { + $perfect_match = preg_match( $pattern, $url, $elements ); + if ( $perfect_match ) { + break; + } + } + + return $elements; + } + + /** + * Clean the DocumentCloud URL. + * + * @param string $url + * @return string + */ + function clean_dc_url( $url ) { + $elements = $this->parse_dc_url( $url ); + if ( isset( $elements['document_slug'] ) ) { + $url = "{$elements['protocol']}://" . WP_DocumentCloud::OEMBED_RESOURCE_DOMAIN . "/documents/{$elements['document_slug']}"; + if ( isset( $elements['page_number'] ) ) { + $url .= "/pages/{$elements['page_number']}"; + } else if ( isset( $elements['note_id'] ) ) { + $url .= "/annotations/{$elements['note_id']}"; + } + $url .= '.html'; + } + return $url; + } + + /** + * Add the DocumentCloud options page. + */ + function add_options_page() { + if ( current_user_can( 'manage_options' ) ) { + add_options_page( 'DocumentCloud', 'DocumentCloud', 'manage_options', 'documentcloud', array( $this, 'render_options_page' ) ); + } + } + + /** + * Render the DocumentCloud options page. + */ + function render_options_page() { + ?> +

+
+ +

responsive="false" on an embed.', 'documentcloud' ) ) ?>

+ + + + +

+
+ get_default_sizes(); + echo ''; + } + + /** + * Render the default width field. + */ + function default_width_field() { + $default_sizes = $this->get_default_sizes(); + echo ''; + } + + /** + * Render the full width field. + */ + function full_width_field() { + $default_sizes = $this->get_default_sizes(); + echo ''; + } +} + +new WP_DocumentCloud; diff --git a/js/button.png b/js/button.png deleted file mode 100644 index d87690f..0000000 Binary files a/js/button.png and /dev/null differ diff --git a/js/navis-documentcloud-editor-plugin.js b/js/navis-documentcloud-editor-plugin.js deleted file mode 100644 index 8f43513..0000000 --- a/js/navis-documentcloud-editor-plugin.js +++ /dev/null @@ -1,72 +0,0 @@ -(function() { - tinymce.create('tinymce.plugins.DocumentCloud', { - /** - * Initializes the plugin, this will be executed after the plugin has been created. - * This call is done before the editor instance has finished it's initialization so use the onInit event - * of the editor instance to intercept that event. - * - * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. - * @param {string} url Absolute URL to where the plugin is located. - */ - init : function(ed, url) { - // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample'); - ed.addCommand('documentcloud', function() { - - ed.windowManager.open({ - file : url + '/window.php?wpbase=' + userSettings.url, - width : 320, - height : 200, - inline : 1 - }, { - plugin_url : url - }); - }); - - // Register example button - ed.addButton('documentcloud', { - title : 'Document Cloud', - cmd : 'documentcloud', - image : url + '/button.png' - }); - - // Add a node change handler, selects the button in the UI when a image is selected - ed.onNodeChange.add(function(ed, cm, n) { - cm.setActive('documentcloud', n.nodeName == 'IMG'); - }); - }, - - /** - * Creates control instances based in the incomming name. This method is normally not - * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons - * but you sometimes need to create more complex controls like listboxes, split buttons etc then this - * method can be used to create those. - * - * @param {String} n Name of the control to create. - * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control. - * @return {tinymce.ui.Control} New control instance or null if no control was created. - */ - createControl : function(n, cm) { - return null; - }, - - /** - * Returns information about the plugin as a name/value array. - * The current keys are longname, author, authorurl, infourl and version. - * - * @return {Object} Name/value array containing information about the plugin. - */ - getInfo : function() { - return { - longname : 'Navis DocumentCloud Plugin', - author : 'Chris Amico', - authorurl : 'http://stateimpact.npr.org/', - infourl : 'http://stateimpact.npr.org/', - version : "1.0" - }; - } - }); - - // Register plugin - tinymce.PluginManager.add('documentcloud', tinymce.plugins.DocumentCloud); -})(); - diff --git a/js/tinywindow.js b/js/tinywindow.js deleted file mode 100644 index a15860e..0000000 --- a/js/tinywindow.js +++ /dev/null @@ -1,24 +0,0 @@ -function init() { - tinyMCEPopup.resizeToInnerSize(); -} - -function insertDocumentCode() { - var inst = tinyMCE.getInstanceById('content'); - var html = inst.selection.getContent(); - - // var url = $('input[name]=url').val(); - var shortcode = "[documentcloud " - + shortcode_format('url', $('input#url').val()) - + shortcode_format('format', $('select#format').val()) - + shortcode_format('sidebar', $('input#sidebar:checked').val() || false) - + "]"; - - window.tinyMCE.execInstanceCommand('content', 'mceInsertContent', false, shortcode); - tinyMCEPopup.editor.execCommand('mceRepaint'); - tinyMCEPopup.close(); - return; -} - -function shortcode_format(key, value) { - return key + "=" + value + " "; -} \ No newline at end of file diff --git a/js/window.php b/js/window.php deleted file mode 100644 index ce71cfd..0000000 --- a/js/window.php +++ /dev/null @@ -1,75 +0,0 @@ - - - - - Insert a Document - - - - - - - -
-

- - -

-

- - -

-

- - -

-
-
- -
-
- -
-
-
- - - - - diff --git a/navis-documentcloud.php b/navis-documentcloud.php deleted file mode 100644 index c1c7aad..0000000 --- a/navis-documentcloud.php +++ /dev/null @@ -1,247 +0,0 @@ - null, - 'id' => null, - 'height' => get_option('documentcloud_default_height', 600), - 'width' => get_option('documentcloud_default_width', 620), - 'format' => 'normal', - 'sidebar' => 'false', - 'text' => 'true', - 'pdf' => 'true' - ); - } - - function add_options_page() { - add_options_page('DocumentCloud', 'DocumentCloud', 'manage_options', - 'documentcloud', array(&$this, 'render_options_page')); - } - - function render_options_page() { ?> -

DocumentCloud Options

-
- - - - -

-
- "; - } - - function default_width_field() { - $option = intval(get_option( 'documentcloud_default_width', 620 )); - echo ""; - } - - function full_width_field() { - $option = intval(get_option( 'documentcloud_full_width', 620 )); - echo ""; - } - - function settings_section() {} - - function save($post_id) { - // tell the post if we're carrying a wide load - - $post = get_post($post_id); - - // avoid autosave - if ( !in_array( $post->post_status, array( - 'publish', 'draft', 'private', 'future', 'pending' - )) - ) { return; } - - $defaults = $this->get_defaults(); - $wide_assets = get_post_meta($post_id, 'wide_assets', true); - $documents = get_post_meta($post_id, 'documentcloud', true); - $matches = array(); - - preg_match_all('/'.get_shortcode_regex().'/', $post->post_content, $matches); - $tags = $matches[2]; - $args = $matches[3]; - foreach($tags as $i => $tag) { - if ($tag == "documentcloud") { - $atts = shortcode_parse_atts($args[$i]); - $atts = shortcode_atts($defaults, $atts); - - // get a doc id to keep array keys consistent - if ( isset($atts['url']) && !isset($atts['id']) ) { - $atts['id'] = $this->parse_id_from_url($atts['url']); - } - - // if no id, don't bother storing because it's wrong - if ($atts['id'] != null) { - if ( $atts['format'] == "wide" || $atts['width'] > $defaults['width']) { - $wide_assets[$atts['id']] = true; - } else { - $wide_assets[$atts['id']] = false; - } - - $documents[$atts['id']] = $atts; - - } - } - } - update_post_meta($post_id, 'documents', $documents); - update_post_meta($post_id, 'wide_assets', $wide_assets); - - } - - function parse_id_from_url($url) { - $regex = '{^https://www\.documentcloud\.org/documents/(?P.+)\.html}'; - $matches = array(); - if (preg_match($regex, $url, $matches)) { - return $matches['id']; - } else { - return null; - } - } - - function embed_shortcode($atts, $content, $code) { - global $post; - $defaults = $this->get_defaults(); - extract( shortcode_atts($defaults, $atts)); - - // we need a document ID or URL, or it's a no op - if ($url && !$id) { - // parse id from url - $id = $this->parse_id_from_url($url); - } - - // still no id? nothin doing - if (!$id) return; - - # we only deal with integers - $height = intval($height); - $width = intval($width); - if ($format == "wide") { - $width = get_option('documentcloud_full_width', 940); - } - - $is_wide = $width > $defaults['width']; - - // full control in single templates - if (is_single() || is_page()) { - return " -
- - - "; - } else { - // index view is always normal width, no sidebar - return " -
- - - "; - } - } -} - -new Navis_DocumentCloud; \ No newline at end of file diff --git a/readme.txt b/readme.txt index 3164c4d..312c46d 100644 --- a/readme.txt +++ b/readme.txt @@ -1,44 +1,134 @@ -=== Navis DocumentCloud === -Contributors: chrisamico -Tags: documentcloud, documents -Requires at least: 3.2 -Tested up to: 3.3.2 +=== DocumentCloud === +Contributors: chrisamico, reefdog +Tags: documentcloud, documents, journalism, reporting, research +Requires at least: 3.5 +Tested up to: 4.7 Stable tag: trunk +License: GPLv2 +License URI: http://www.gnu.org/licenses/gpl-2.0.html -Embed DocumentCloud documents that won't be eaten by the visual editor. +Embed DocumentCloud resources in WordPress content. == Description == -[DocumentCloud](http://www.documentcloud.org/home) is a free service allowing journalists to analyze, annotate and publish documents, funded by the Knight Foundation. Initial development supported by [NPR](http://www.npr.org) as part of [StateImpact](http://stateimpact.npr.org) project. +[DocumentCloud](https://www.documentcloud.org/) is a service that allows journalists to analyze, annotate and publish documents, hosted by Investigative Reporters & Editors. Initial development of this plugin supported by [NPR](http://www.npr.org) as part of the [StateImpact](http://stateimpact.npr.org) project. -DocumentCloud's normal embed code looks like this: +This plugin allows you to embed DocumentCloud resources using either the raw URL on its own line: -
- - - -That works great as long as you edit in HTML mode. Switch to the visual editor, and your container `div` disappears and your JavaScript is broken. + Here's something you should really take a look at: -To get around this, use this short code: + https://www.documentcloud.org/documents/282753-lefler-thesis.html - [documentcloud id="265231-11-07-2011-letter-to-idaho-congressional"] - -Or use the URL from DocumentCloud: + Isn't that interesting? - [documentcloud url="http://www.documentcloud.org/documents/265231-11-07-2011-letter-to-idaho-congressional.html"] +Or a custom shortcode: + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html"] +When you save, WordPress fetches and stores the actual embed code HTML from the DocumentCloud servers using oEmbed. You can freely toggle between visual and HTML mode without mangling embed code, and your embed will always be up to date with the latest embed code. + +By default, documents will have a responsive width (it will narrow and widen as necessary to fill available content area) and use the theme's default height. If you want to override this, you can either set `responsive="false"` or explicitly set a `width`: + + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html" width="600"] + +You can set your own defaults in Settings > DocumentCloud, but default widths will be ignored unless `responsive` is disabled: + + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html" responsive="false"] + +To embed a single page, use any page-specific URL. Pages ignore `width/height` and always act responsively: + + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html#document/p1"] + +To embed a note, use any note-specific URL. Notes ignore `width/height` and always act responsively: + + [documentcloud url="https://www.documentcloud.org/documents/282753-lefler-thesis.html#document/p1/a53674"] + +Here's the full list of embed options you can pass via shortcode attributes; some are specific to the type of resource you're embedding. + +**All resources:** + +- `url` (**required**, string): Full URL of the DocumentCloud resource. +- `container` (string): ID of element to insert the embed into; if excluded, embedder will create its own container. + +**Documents only:** + +- `height` (integer): Height (in pixels) of the embed. +- `width` (integer): Width (in pixels) of the embed. If used, will implicitly set `responsive="false"`. +- `responsive` (boolean): Use responsive layout, which dynamically adjusts width to fill content area. Defaults `true`. +- `responsive_offset` (integer): Distance (in pixels) to vertically offset the viewer for some responsive embeds. +- `page` (integer): Page number to have the document scroll to by default. +- `note` (integer): ID of the note that the document should highlight by default. +- `notes` (boolean): Hide or show notes. +- `search` (boolean): Hide or show search form. +- `sidebar` (boolean): Hide or show sidebar. +- `pdf` (boolean): Hide or show link to download original PDF. +- `text` (boolean): Hide or show text tab. +- `zoom` (boolean): Hide or show zoom slider. +- `format` (string): Indicate to the theme that this is a wide asset by setting this to `wide`. Defaults `normal`. + +You can read more about publishing and embedding DocumentCloud resources on https://www.documentcloud.org/help/publishing. == Installation == -1. Upload the navis-documentcloud directory to `wp-content/plugins/navis-documentcloud` -2. Activate the plugin -3. In your posts, add documents using the DocumentCloud button, or the `[documentcloud]` shortcode. +1. Upload the contents of the plugin to `wp-content/plugins/documentcloud` +2. Activate the plugin through the "Plugins" menu +3. In your posts, embed documents, pages, or notes using the DocumentCloud button or the `[documentcloud]` shortcode +4. Optional: Set a default width/height for all DocumentCloud embeds (which can be overridden on a per-embed basis with the `height/width` attributes) at Settings > DocumentCloud. (This default width will only be used if you set `responsive="false"` on an embed.) + +**Upgrading from Navis DocumentCloud:** If you're currently using the Navis DocumentCloud plugin (from which this plugin was built), you'll want to deactivate or delete it before installing this plugin. + +== Changelog == + += 0.4.3 = +* Separate the oEmbed config options (provided as params to the endpoint) from the embed config options (encoded as params on the `url`) (#48) +* Rename `default_page` and `default_note` options back to `page` and `note` (#47) +* Remove `sidebar`, `text`, and `pdf` default values + += 0.4.2 = +* Recognize Unicode document slugs (#37) + += 0.4.1 = +* Conform syntax to WordPress VIP plugin requirements (#30) (@bcampeau) +* Fixed: Bare URLs now get default parameters (#35) +* Fixed: Pages/notes on docs with uppercase slugs now embeddable (#36) + += 0.4.0 = +* Support embedding pages (#28) +* Support embedding raw contextual page/note URLs (#29) +* Check for existence of things to stop triggering PHP notices (#27) +* Add DocumentCloud logo for plugin (#26) +* Fix ability to specify a container in the shortcode +* Improve embeddable resource pattern recognition + += 0.3.3 = +* Remove unused TinyMCE components +* Resolve CVE-2015-2807 reported by dxw Security at https://security.dxw.com/advisories/publicly-exploitable-xss-in-wordpress-plugin-navis-documentcloud/ + += 0.3.2 = +* Implement a few best practice security measures + += 0.3.1 = +* Check for old (Navis) plugin and warn admins of conflict +* Add note about raw URLs to README +* Stop storing shortcode attributes in the `postmeta` table + += 0.3 = +* Add support for embedding notes. +* Default to responsive. +* Enable caching. + += 0.2 = +* Fetch embed code via oEmbed instead of generating statically. +* Add new options: `container`, `responsive`, `responsive_offset`, `default_page`, `default_note`, `notes`, `search`, and `zoom`. +* Deprecate `id` attribute. It's still usable, but support may drop in the future. Use `url` instead. + += 0.1 = +* Initial release. + +== Upgrade Notice == + += 0.3 = +Adds support for embedding notes and enables caching. += 0.2 = +Adds oEmbed support for future-proofing embed codes. Provides additional embed options like `default_page`.