├── .gitignore ├── screenshot-1.png ├── screenshot-2.png ├── screenshot-3.jpg ├── screenshot-4.png ├── img └── system │ ├── film.png │ ├── left.gif │ ├── link.png │ ├── plus.png │ ├── camera.png │ ├── cancel.png │ ├── right.gif │ ├── document.png │ ├── theme-new.png │ ├── waiting.gif │ ├── theme-round.png │ ├── document-small.png │ └── theme-legacy.png ├── css ├── external │ ├── loading.gif │ ├── font │ │ ├── bpfb.eot │ │ ├── bpfb.ttf │ │ ├── bpfb.woff │ │ └── bpfb.svg │ └── fileuploader.css ├── admin.css ├── bpfb_interface.css └── bpfb_toolbar.css ├── .gitmodules ├── package.json ├── Gruntfile.js ├── lib ├── forms │ ├── link_tag_template.php │ └── images_tag_template.php ├── class_bpfb_installer.php ├── class_bpfb_data.php ├── external │ ├── file_uploader.php │ └── simple_html_dom.php ├── class_bpfb_codec.php ├── class_bpfb_admin_pages.php ├── bpfb_group_documents.php └── class_bpfb_binder.php ├── js ├── bpfb_group_documents.js ├── bpfb_interface.js └── external │ └── fileuploader.js ├── dev-readme.txt ├── languages └── bpfb-default.po ├── bpfb.php ├── README.md └── license.txt /.gitignore: -------------------------------------------------------------------------------- 1 | TODO 2 | node_modules 3 | -------------------------------------------------------------------------------- /screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/screenshot-1.png -------------------------------------------------------------------------------- /screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/screenshot-2.png -------------------------------------------------------------------------------- /screenshot-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/screenshot-3.jpg -------------------------------------------------------------------------------- /screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/screenshot-4.png -------------------------------------------------------------------------------- /img/system/film.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/film.png -------------------------------------------------------------------------------- /img/system/left.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/left.gif -------------------------------------------------------------------------------- /img/system/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/link.png -------------------------------------------------------------------------------- /img/system/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/plus.png -------------------------------------------------------------------------------- /img/system/camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/camera.png -------------------------------------------------------------------------------- /img/system/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/cancel.png -------------------------------------------------------------------------------- /img/system/right.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/right.gif -------------------------------------------------------------------------------- /css/external/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/css/external/loading.gif -------------------------------------------------------------------------------- /img/system/document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/document.png -------------------------------------------------------------------------------- /img/system/theme-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/theme-new.png -------------------------------------------------------------------------------- /img/system/waiting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/waiting.gif -------------------------------------------------------------------------------- /css/external/font/bpfb.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/css/external/font/bpfb.eot -------------------------------------------------------------------------------- /css/external/font/bpfb.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/css/external/font/bpfb.ttf -------------------------------------------------------------------------------- /img/system/theme-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/theme-round.png -------------------------------------------------------------------------------- /css/external/font/bpfb.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/css/external/font/bpfb.woff -------------------------------------------------------------------------------- /img/system/document-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/document-small.png -------------------------------------------------------------------------------- /img/system/theme-legacy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/buddypress-activity-plus/master/img/system/theme-legacy.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/external/dash"] 2 | path = lib/external/dash 3 | url = git@bitbucket.org:incsub/wpmudev-dashboard-notification.git 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "activity-plus", 3 | "version": "1.6.5", 4 | "description": "A Facebook-style media sharing improvement for the activity box.", 5 | "main": "Gruntfile.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "WPMU DEV", 10 | "license": "GPL", 11 | "devDependencies": { 12 | "grunt": "^1.0.1", 13 | "grunt-wp-i18n": "^1.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(grunt) { 4 | 5 | grunt.loadNpmTasks('grunt-wp-i18n'); 6 | 7 | grunt.initConfig({ 8 | makepot: { 9 | target: { 10 | options: { 11 | domainPath: 'languages/', 12 | type: 'wp-plugin', 13 | potFilename: 'bpfb-default.po', 14 | exclude: ['lib/external'], 15 | potHeaders: { 16 | 'report-msgid-bugs-to': '\n', 17 | 'project-id-version': 'Activity Plus\n' 18 | } 19 | } 20 | } 21 | } 22 | }); 23 | grunt.registerTask('default', ['makepot']); 24 | }; 25 | -------------------------------------------------------------------------------- /lib/forms/link_tag_template.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/admin.css: -------------------------------------------------------------------------------- 1 | .bpfb fieldset.section { 2 | margin: 2em; 3 | margin-top: 0; 4 | } 5 | .bpfb fieldset.section legend { 6 | font-size: 2em; 7 | margin: 1em 0; 8 | margin-top: 0; 9 | } 10 | 11 | .bpfb fieldset.option { 12 | margin-top: 1em; 13 | border: 1px solid #ddd; 14 | padding: 1em; 15 | } 16 | .bpfb fieldset.option legend { 17 | font-size: 1.4em; 18 | margin: 0; 19 | padding: 0 1em; 20 | } 21 | 22 | .bpfb fieldset.option label { 23 | display: block; 24 | } 25 | 26 | .bpfb .theme.option img { 27 | display: block; 28 | margin: 0 auto; 29 | } 30 | .bpfb .theme.option label { 31 | width: 150px; 32 | float: left; 33 | text-align: center; 34 | background: #eee; 35 | padding: 5px; 36 | margin-right: 15px; 37 | margin-bottom: 5px; 38 | border: 1px solid #ddd; 39 | } -------------------------------------------------------------------------------- /lib/forms/images_tag_template.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | > 17 | 18 | 19 | 20 | 21 |
-------------------------------------------------------------------------------- /css/bpfb_interface.css: -------------------------------------------------------------------------------- 1 | .bpfb_form_container { 2 | padding: 1em; 3 | background-color: #eee; 4 | } 5 | .bpfb_actions_container, .bpfb_preview_container { 6 | margin-bottom: 1em; 7 | } 8 | input.bpfb_primary_button, input.bpfb_primary_button:hover { 9 | background: #5fb3c9; 10 | color: #fff; 11 | } 12 | .bpfb_waiting { 13 | width: 100%; 14 | height: 50px; 15 | background: url(../img/system/waiting.gif) center top no-repeat; 16 | } 17 | 18 | #bpfb_video_url, #bpfb_link_preview_url { 19 | color: #ccc; 20 | font-style: italic; 21 | } 22 | #bpfb_video_url.changed, #bpfb_link_preview_url.changed { 23 | color: #333; 24 | font-style: normal; 25 | } 26 | 27 | .bpfb_link_preview_title { 28 | font-weight: bold; 29 | font-size: 1.1em; 30 | } 31 | .bpfb_link_preview_url { 32 | color: #666; 33 | font-size: .8em; 34 | } 35 | .bpfb_link_preview_container { 36 | width: 180px; 37 | height: 135px; 38 | overflow: hidden; 39 | } 40 | 41 | .bpfb_controls_container .qq-upload-button { 42 | background: #5fb3c9; 43 | color: #fff; 44 | padding: 3px; 45 | } 46 | .bpfb_preview_photo_item { 47 | margin-right: 5px; 48 | border: 3px solid #fff; 49 | } 50 | 51 | 52 | .bpfb_final_link .bpfb_link_preview_container { 53 | float: left; 54 | } 55 | .bpfb_final_link .bpfb_link_contents { 56 | width: 400px; 57 | float: left; 58 | margin-left: 10px; 59 | } 60 | 61 | #bpfb-too_many_photos { 62 | font-weight: bold; 63 | font-style: italic; 64 | } -------------------------------------------------------------------------------- /css/external/fileuploader.css: -------------------------------------------------------------------------------- 1 | .bpfb_controls_container .qq-uploader { position:relative; width: 100%;} 2 | 3 | .bpfb_controls_container .qq-upload-button { 4 | display:block; /* or inline-block */ 5 | width: 105px; padding: 7px 0; text-align:center; 6 | background:#880000; border-bottom:1px solid #ddd;color:#fff; 7 | } 8 | .bpfb_controls_container .qq-upload-button-hover {background:#cc0000;} 9 | .bpfb_controls_container .qq-upload-button-focus {outline:1px dotted black;} 10 | 11 | .bpfb_controls_container .qq-upload-drop-area { 12 | position:absolute; top:0; left:0; width:100%; height:100%; min-height: 70px; z-index:2; 13 | background:#FF9797; text-align:center; 14 | } 15 | .bpfb_controls_container .qq-upload-drop-area span { 16 | display:block; position:absolute; top: 50%; width:100%; margin-top:-8px; font-size:16px; 17 | } 18 | .bpfb_controls_container .qq-upload-drop-area-active {background:#FF7171;} 19 | 20 | .bpfb_controls_container .qq-upload-list {margin:15px 35px; padding:0; list-style:disc;} 21 | .bpfb_controls_container .qq-upload-list li { margin:0; padding:0; line-height:15px; font-size:12px;} 22 | .bpfb_controls_container .qq-upload-file, .bpfb_controls_container .qq-upload-spinner, .bpfb_controls_container .qq-upload-size, .bpfb_controls_container .qq-upload-cancel, .bpfb_controls_container .qq-upload-failed-text { 23 | margin-right: 7px; 24 | } 25 | 26 | .bpfb_controls_container .qq-upload-file {} 27 | .bpfb_controls_container .qq-upload-spinner {display:inline-block; background: url("loading.gif"); width:15px; height:15px; vertical-align:text-bottom;} 28 | .bpfb_controls_container .qq-upload-size,.bpfb_controls_container .qq-upload-cancel {font-size:11px;} 29 | 30 | .bpfb_controls_container .qq-upload-failed-text {display:none;} 31 | .bpfb_controls_container .qq-upload-fail .qq-upload-failed-text {display:inline;} -------------------------------------------------------------------------------- /lib/class_bpfb_installer.php: -------------------------------------------------------------------------------- 1 | prepare_paths()) { 19 | $me->set_default_options(); 20 | } else $me->kill_default_options(); 21 | } 22 | 23 | /** 24 | * Checks to see if the plugin is installed. 25 | * 26 | * If not, installs it. 27 | * 28 | * @access public 29 | * @static 30 | */ 31 | static function check () { 32 | $is_installed = get_option('bpfb_plugin', false); 33 | if (!$is_installed) return BpfbInstaller::install(); 34 | if (!BpfbInstaller::check_paths()) return BpfbInstaller::install(); 35 | return true; 36 | } 37 | 38 | /** 39 | * Checks to see if we have the proper paths and if they're writable. 40 | * 41 | * @access private 42 | */ 43 | static function check_paths () { 44 | if (!file_exists(BPFB_TEMP_IMAGE_DIR)) return false; 45 | if (!file_exists(BPFB_BASE_IMAGE_DIR)) return false; 46 | if (!is_writable(BPFB_TEMP_IMAGE_DIR)) return false; 47 | if (!is_writable(BPFB_BASE_IMAGE_DIR)) return false; 48 | return true; 49 | } 50 | 51 | /** 52 | * Prepares paths that will be used. 53 | * 54 | * @access private 55 | */ 56 | function prepare_paths () { 57 | $ret = true; 58 | 59 | if (!file_exists(BPFB_TEMP_IMAGE_DIR)) $ret = wp_mkdir_p(BPFB_TEMP_IMAGE_DIR); 60 | if (!$ret) return false; 61 | 62 | if (!file_exists(BPFB_BASE_IMAGE_DIR)) $ret = wp_mkdir_p(BPFB_BASE_IMAGE_DIR); 63 | if (!$ret) return false; 64 | 65 | return true; 66 | } 67 | 68 | /** 69 | * (Re)sets Plugin options to defaults. 70 | * 71 | * @access private 72 | */ 73 | function set_default_options () { 74 | $options = array ( 75 | 'installed' => 1, 76 | ); 77 | update_option('bpfb_plugin', $options); 78 | } 79 | 80 | /** 81 | * Removes plugin default options. 82 | */ 83 | function kill_default_options () { 84 | delete_option('bpfb_plugin'); 85 | } 86 | } -------------------------------------------------------------------------------- /lib/class_bpfb_data.php: -------------------------------------------------------------------------------- 1 | _data = wp_parse_args($data, array( 10 | 'oembed_width' => 450, 11 | 'image_limit' => 5, 12 | 'links_target' => false, 13 | 'cleanup_images' => false, 14 | )); 15 | } 16 | 17 | public function get ($option, $fallback=false) { 18 | $define = 'BPFB_' . strtoupper($option); 19 | if (defined($define)) return constant($define); 20 | return $this->_get($option, $fallback); 21 | } 22 | 23 | public function get_strict ($option, $fallback=false) { 24 | return $this->_get($option, $fallback); 25 | } 26 | 27 | public function get_thumbnail_size ($strict=false) { 28 | $thumb_w = empty($this->_data['thumbnail_size_width']) || !(int)$this->_data['thumbnail_size_width'] 29 | ? get_option('thumbnail_size_w', 100) 30 | : (int)$this->_data['thumbnail_size_width'] 31 | ; 32 | $thumb_w = $thumb_w ? $thumb_w : 100; 33 | $thumb_h = empty($this->_data['thumbnail_size_height']) || !(int)$this->_data['thumbnail_size_height'] 34 | ? get_option('thumbnail_size_h', 100) 35 | : (int)$this->_data['thumbnail_size_height'] 36 | ; 37 | $thumb_h = $thumb_h ? $thumb_h : 100; 38 | 39 | // Override thumbnail image size in wp-config.php 40 | if (!$strict && defined('BPFB_THUMBNAIL_IMAGE_SIZE')) { 41 | list($tw,$th) = explode('x', BPFB_THUMBNAIL_IMAGE_SIZE); 42 | $thumb_w = (int)$tw ? (int)$tw : $thumb_w; 43 | $thumb_h = (int)$th ? (int)$th : $thumb_h; 44 | } 45 | 46 | return array($thumb_w, $thumb_h); 47 | } 48 | 49 | private function _get ($option, $fallback=false) { 50 | if (isset($this->_data[$option])) return $this->_data[$option]; 51 | return $fallback; 52 | } 53 | } 54 | 55 | class Bpfb_Data { 56 | 57 | private static $_instance; 58 | 59 | private function __construct() {} 60 | private function __clone() {} 61 | 62 | public static function get ($option, $fallback=false) { 63 | if (!self::$_instance) self::_spawn_instance(); 64 | return self::$_instance->get($option, $fallback); 65 | } 66 | 67 | public static function get_strict ($option, $fallback=false) { 68 | if (!self::$_instance) self::_spawn_instance(); 69 | return self::$_instance->get_strict($option, $fallback); 70 | } 71 | 72 | public static function get_thumbnail_size ($strict=false) { 73 | if (!self::$_instance) self::_spawn_instance(); 74 | return self::$_instance->get_thumbnail_size($strict); 75 | } 76 | 77 | private static function _spawn_instance () { 78 | if (self::$_instance) return false; 79 | self::$_instance = new Bpfb_Data_Container; 80 | } 81 | } -------------------------------------------------------------------------------- /js/bpfb_group_documents.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | $(function () { 3 | 4 | var BpfbDocumentHandler = function () { 5 | $container = $(".bpfb_controls_container"); 6 | 7 | var createMarkup = function () { 8 | var html = '
' + 9 | '' 10 | ; 11 | $container.append(html); 12 | 13 | var uploader = new qq.FileUploader({ 14 | "element": $('#bpfb_tmp_document').get(0), 15 | "listElement": $('#bpfb_tmp_document_list')[0], 16 | "allowedExtensions": _bpfbDocumentsAllowedExtensions, 17 | "action": ajaxurl, 18 | "params": { 19 | "action": "bpfb_preview_document" 20 | }, 21 | "onComplete": createDocumentPreview 22 | }); 23 | 24 | }; 25 | 26 | var createDocumentPreview = function (id, fileName, resp) { 27 | if ("error" in resp) return false; 28 | var html = ' ' + resp.file + '
' + 29 | ''; 30 | $('.bpfb_preview_container').append(html); 31 | $('.bpfb_action_container').html( 32 | '

' + 33 | '

' 34 | ); 35 | $("#bpfb_cancel_action").hide(); 36 | }; 37 | 38 | var removeTempDocuments = function (rti_callback) { 39 | var $docs = $('input.bpfb_documents_to_add'); 40 | if (!$docs.length) return rti_callback(); 41 | $.post(ajaxurl, {"action":"bpfb_remove_temp_documents", "data": $docs.serialize().replace(/%5B%5D/g, '[]')}, function (data) { 42 | rti_callback(); 43 | }); 44 | }; 45 | 46 | var processForSave = function () { 47 | var $docs = $('input.bpfb_documents_to_add'); 48 | var docArr = []; 49 | $docs.each(function () { 50 | docArr[docArr.length] = $(this).val(); 51 | }); 52 | return { 53 | "bpfb_documents": docArr//$imgs.serialize().replace(/%5B%5D/g, '[]') 54 | }; 55 | }; 56 | 57 | var init = function () { 58 | $container.empty(); 59 | $('.bpfb_preview_container').empty(); 60 | $('.bpfb_action_container').empty(); 61 | $('#aw-whats-new-submit').hide(); 62 | createMarkup(); 63 | }; 64 | 65 | var destroy = function () { 66 | removeTempDocuments(function() { 67 | $container.empty(); 68 | $('.bpfb_preview_container').empty(); 69 | $('.bpfb_action_container').empty(); 70 | $('#aw-whats-new-submit').show(); 71 | }); 72 | }; 73 | 74 | removeTempDocuments(init); 75 | 76 | return {"destroy": destroy, "get": processForSave}; 77 | }; 78 | 79 | $(".bpfb_toolbar_container").append( 80 | ' ' + 81 | '' + l10nBpfbDocs.add_documents + '' 82 | ); 83 | 84 | $('#bpfb_addDocuments').click(function () { 85 | if (_bpfbActiveHandler) _bpfbActiveHandler.destroy(); 86 | var group_id = $('#whats-new-post-in').length ? $('#whats-new-post-in').val() : 0; 87 | if (parseInt(group_id)) { 88 | _bpfbActiveHandler = new BpfbDocumentHandler(); 89 | $("#bpfb_cancel_action").show(); 90 | } else { 91 | alert(l10nBpfbDocs.no_group_selected); 92 | } 93 | return false; 94 | }); 95 | 96 | }); 97 | })(jQuery); -------------------------------------------------------------------------------- /dev-readme.txt: -------------------------------------------------------------------------------- 1 | === BuddyPress Activity Plus === 2 | Contributors: WPMUDEV 3 | Tags: BuddyPress, Activity, Activity Stream, BuddyPress Activity, Wall, Embed, Media, Youtube, Photos, Facebook, Social Network, Social Networking, Embed Video, Embed Link, Upload Photo, Upload Photos, Share Media, Sharing Media, Social Network Wall, Social Media 4 | Requires at least: 3.1 5 | Tested up to: 4.7 6 | Stable tag: 1.6.5 7 | 8 | BuddyPress Activity Plus allows for embedding of oEmbed videos and media in your activities. 9 | 10 | == Description == 11 | 12 | BuddyPress Embed Activity gives your social network all the features and ease of Facebook when it comes to uploading and sharing media! 13 | 14 | [youtube https://www.youtube.com/watch?v=57qUenN1DOM] 15 | 16 | The plugin adds 3 new buttons to your BuddyPress activity stream. Enabling you to attach photos, videos, and even share web links with everyone on your network! 17 | 18 | Here's the quick overview of this plugin's features: 19 | * Upload a photo (or multiple) directly from your computer to the activity stream 20 | * Embed a video from popular sites such as youtube and vimeo by copying the link 21 | * Embed a link to any site - the site title and description will automatically be pulled in 22 | * Embedding a link also allows you to choose a thumbnail image from a list of images on the site's homepage 23 | * Works perfectly with any theme based on the BuddyPress Default theme 24 | 25 | == Installation == 26 | = To Install: = 27 | 28 | 1. Download the plugin file 29 | 2. Unzip the file into a folder on your hard drive 30 | 3. Upload the `/bpfb/` folder to the `/wp-content/plugins/` folder on your site 31 | 4. Single-site BuddyPres go to Plugins menu and activate there. 32 | 5. For Multisite visit Network Admin -> Plugins and Network Activate it there. 33 | 34 | == Frequently Asked Questions == 35 | 36 | = Do I need to be a paid WPMU DEV member? = 37 | No. This plugin is offered as is at no charge. 38 | 39 | = How do I get support? = 40 | We provide comprehensive and guaranteed support on the WPMU DEV forums and live chat only. 41 | 42 | == Screenshots == 43 | 44 | 1. Embed Activity Window 45 | 2. Photos and websites are easily embedded 46 | 3. Image galleries right in the activity stream 47 | 4. Video in your activity stream 48 | 49 | == Changelog == 50 | 51 | = 1.6.5 = 52 | - Fix: override default WP user agent in GET requests 53 | - Fix: better HTTP error handling 54 | - Fix: compat with BP Reshare 55 | - Fix: custom moderation to avoid issues with thumbnails 56 | 57 | = 1.6.4 = 58 | - Fix for preview URL escaping issue. 59 | - Expose dependencies manipulation filters. 60 | 61 | = 1.6.3 = 62 | - Fix for link contents render after editing. 63 | - Fix for certain characters cutting shortcode too short. 64 | 65 | = 1.6.2 = 66 | - Fix for group document uploads. 67 | - Fix for potential CSRF issue and attribute escaping. 68 | - Some cleanup. 69 | 70 | = 1.6.1 = 71 | - Added ability to auto-clean up unused images. 72 | - Some code tweaks and fixes. 73 | 74 | = 1.6 = 75 | - Added themes and the settings page. 76 | - Added EXIF orientation support. 77 | - Better OpenGraph support for link sharing. 78 | 79 | = 1.5 = 80 | - Trimming images for auto-innjected breaks. 81 | - Link type hrefs target assigned for external and all keywords. 82 | - Refactoring the deprecated resizing mehtod. 83 | - Fixing BuddyPress theme compat layer updates. 84 | 85 | = 1.4.1 = 86 | - Fix for shortcodes appearing in activity RSS feeds. 87 | - Wrapping initialization in mobile browser detection stub. 88 | - File name sanitization (thanks, buzug). 89 | 90 | = 1.4 = 91 | - Fixed conflict with duplicated uploader dependencies. 92 | - Prepared the uploader for l10n. 93 | - Added image limit define. 94 | - Added protocol replacement. 95 | 96 | = 1.3.1 = 97 | - Added URL scheme prefixing for links that miss it. 98 | - Added switch for disabling Thickbox. 99 | - Added waiting indicator for link and video processing. 100 | - Enter in video/link input triggers preview. 101 | 102 | 103 | = 1.3 = 104 | - Included missing file resource. 105 | - Re-styled the interface so the icons can be easily changed. 106 | - Added support for theme to force its own styles. 107 | - Added OpenGraph image support to link image detection. 108 | - Added option for overriding the default thumbnail image. 109 | 110 | = 1.2.1 = 111 | BuddyPress v1.5 compatibility update. 112 | 113 | = 1.2 = 114 | * Clearer display of cancel button vs. old 'X' icon 115 | * Progress indicator for file uploads 116 | * Integration with Group Documents plugin. 117 | 118 | = 1.1 = 119 | * Added remote image support. 120 | * Upload images cancels out remote image embedding, and vice versa. 121 | * Remote url input width fix. 122 | * Added support for user-defined oEmbed width. 123 | * Added "no thumbnail" option for link sharing. 124 | 125 | = 1.0 = 126 | Initial Release 127 | -------------------------------------------------------------------------------- /languages/bpfb-default.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017 2 | # This file is distributed under the same license as the package. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: Activity Plus\n" 6 | "Report-Msgid-Bugs-To: \n" 7 | "POT-Creation-Date: 2017-09-13 04:34:37+00:00\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=utf-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "PO-Revision-Date: 2017-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: LANGUAGE \n" 14 | "X-Generator: grunt-wp-i18n1.0.0\n" 15 | 16 | #: bpfb.php:50 17 | msgid "" 18 | "There was an issue determining where BuddyPress Activity Plus plugin is " 19 | "installed. Please reinstall." 20 | msgstr "" 21 | 22 | #: lib/bpfb_group_documents.php:48 23 | msgid "Add documents" 24 | msgstr "" 25 | 26 | #: lib/bpfb_group_documents.php:49 27 | msgid "Please select a group to upload to" 28 | msgstr "" 29 | 30 | #: lib/bpfb_group_documents.php:201 31 | msgid "%s uploaded new file(s): %s to %s" 32 | msgstr "" 33 | 34 | #: lib/class_bpfb_admin_pages.php:32 35 | msgid "BuddyPress Activity Plus" 36 | msgstr "" 37 | 38 | #: lib/class_bpfb_admin_pages.php:33 39 | msgid "Activity Plus" 40 | msgstr "" 41 | 42 | #: lib/class_bpfb_admin_pages.php:92 43 | msgid "Appearance" 44 | msgstr "" 45 | 46 | #: lib/class_bpfb_admin_pages.php:96 47 | msgid "" 48 | "Your BuddyPress theme incorporates Activity Plus style overrides. " 49 | "Respecting the selection you make in the "Appearance" section is " 50 | "entirely up to your theme." 51 | msgstr "" 52 | 53 | #: lib/class_bpfb_admin_pages.php:101 54 | msgid "Theme" 55 | msgstr "" 56 | 57 | #: lib/class_bpfb_admin_pages.php:105 58 | msgid "Default (legacy)" 59 | msgstr "" 60 | 61 | #: lib/class_bpfb_admin_pages.php:110 62 | msgid "New" 63 | msgstr "" 64 | 65 | #: lib/class_bpfb_admin_pages.php:115 66 | msgid "Round" 67 | msgstr "" 68 | 69 | #: lib/class_bpfb_admin_pages.php:119 70 | msgid "Alignment" 71 | msgstr "" 72 | 73 | #: lib/class_bpfb_admin_pages.php:122 74 | msgid "Left" 75 | msgstr "" 76 | 77 | #: lib/class_bpfb_admin_pages.php:126 78 | msgid "Right" 79 | msgstr "" 80 | 81 | #: lib/class_bpfb_admin_pages.php:133 82 | msgid "Functional" 83 | msgstr "" 84 | 85 | #: lib/class_bpfb_admin_pages.php:136 86 | msgid "oEmbed" 87 | msgstr "" 88 | 89 | #: lib/class_bpfb_admin_pages.php:139 90 | msgid "" 91 | "Your oEmbed dimensions will be dictated by the " 92 | "BPFB_OEMBED_WIDTH define value (%s). Remove this define to " 93 | "enable this option." 94 | msgstr "" 95 | 96 | #: lib/class_bpfb_admin_pages.php:143 lib/class_bpfb_admin_pages.php:155 97 | msgid "Width" 98 | msgstr "" 99 | 100 | #: lib/class_bpfb_admin_pages.php:148 101 | msgid "Image thumbnails" 102 | msgstr "" 103 | 104 | #: lib/class_bpfb_admin_pages.php:151 105 | msgid "" 106 | "Your thumbnail dimensions will be dictated by the " 107 | "BPFB_THUMBNAIL_IMAGE_SIZE define value (%s). Remove this " 108 | "define to enable these options." 109 | msgstr "" 110 | 111 | #: lib/class_bpfb_admin_pages.php:159 112 | msgid "Height" 113 | msgstr "" 114 | 115 | #: lib/class_bpfb_admin_pages.php:164 116 | msgid "Misc" 117 | msgstr "" 118 | 119 | #: lib/class_bpfb_admin_pages.php:167 120 | msgid "Clean up images?" 121 | msgstr "" 122 | 123 | #: lib/class_bpfb_admin_pages.php:174 124 | msgid "Save" 125 | msgstr "" 126 | 127 | #: lib/class_bpfb_binder.php:174 128 | msgid "Add images" 129 | msgstr "" 130 | 131 | #: lib/class_bpfb_binder.php:175 132 | msgid "Submit images post" 133 | msgstr "" 134 | 135 | #: lib/class_bpfb_binder.php:176 136 | msgid "Add image URL" 137 | msgstr "" 138 | 139 | #: lib/class_bpfb_binder.php:177 140 | msgid "Add another image URL" 141 | msgstr "" 142 | 143 | #: lib/class_bpfb_binder.php:178 144 | msgid "Add videos" 145 | msgstr "" 146 | 147 | #: lib/class_bpfb_binder.php:179 148 | msgid "Submit video post" 149 | msgstr "" 150 | 151 | #: lib/class_bpfb_binder.php:180 152 | msgid "Add links" 153 | msgstr "" 154 | 155 | #: lib/class_bpfb_binder.php:181 156 | msgid "Submit link post" 157 | msgstr "" 158 | 159 | #: lib/class_bpfb_binder.php:182 160 | msgid "Add" 161 | msgstr "" 162 | 163 | #: lib/class_bpfb_binder.php:183 164 | msgid "Cancel" 165 | msgstr "" 166 | 167 | #: lib/class_bpfb_binder.php:184 168 | msgid "Preview" 169 | msgstr "" 170 | 171 | #: lib/class_bpfb_binder.php:185 172 | msgid "Drop files here to upload" 173 | msgstr "" 174 | 175 | #: lib/class_bpfb_binder.php:186 176 | msgid "Upload a file" 177 | msgstr "" 178 | 179 | #: lib/class_bpfb_binder.php:187 180 | msgid "Choose thumbnail" 181 | msgstr "" 182 | 183 | #: lib/class_bpfb_binder.php:188 184 | msgid "No thumbnail" 185 | msgstr "" 186 | 187 | #: lib/class_bpfb_binder.php:189 188 | msgid "Paste video URL here" 189 | msgstr "" 190 | 191 | #: lib/class_bpfb_binder.php:190 192 | msgid "Paste link here" 193 | msgstr "" 194 | 195 | #: lib/class_bpfb_binder.php:191 196 | msgid "You tried to add too many images, only %d will be posted." 197 | msgstr "" 198 | 199 | #: lib/class_bpfb_binder.php:217 lib/class_bpfb_binder.php:234 200 | msgid "There has been an error processing your request" 201 | msgstr "" 202 | 203 | #: lib/class_bpfb_binder.php:218 lib/class_bpfb_binder.php:235 204 | msgid "Processing..." 205 | msgstr "" -------------------------------------------------------------------------------- /lib/external/file_uploader.php: -------------------------------------------------------------------------------- 1 | getSize()){ 18 | return false; 19 | } 20 | 21 | $target = fopen($path, "w"); 22 | fseek($temp, 0, SEEK_SET); 23 | stream_copy_to_stream($temp, $target); 24 | fclose($target); 25 | 26 | return true; 27 | } 28 | function getName() { 29 | return $_GET['qqfile']; 30 | } 31 | function getSize() { 32 | if (isset($_SERVER["CONTENT_LENGTH"])){ 33 | return (int)$_SERVER["CONTENT_LENGTH"]; 34 | } else { 35 | //throw new Exception('Getting content length is not supported.'); 36 | return false; 37 | } 38 | } 39 | } 40 | 41 | /** 42 | * Handle file uploads via regular form post (uses the $_FILES array) 43 | */ 44 | class qqUploadedFileForm { 45 | /** 46 | * Save the file to the specified path 47 | * @return boolean TRUE on success 48 | */ 49 | function save($path) { 50 | if(!move_uploaded_file($_FILES['qqfile']['tmp_name'], $path)){ 51 | return false; 52 | } 53 | return true; 54 | } 55 | function getName() { 56 | return $_FILES['qqfile']['name']; 57 | } 58 | function getSize() { 59 | return $_FILES['qqfile']['size']; 60 | } 61 | } 62 | 63 | class qqFileUploader { 64 | var $allowedExtensions = array(); 65 | var $sizeLimit = 10485760; 66 | var $file; 67 | 68 | function __construct(array $allowedExtensions = array(), $sizeLimit = 10485760){ 69 | $allowedExtensions = array_map("strtolower", $allowedExtensions); 70 | 71 | $this->allowedExtensions = $allowedExtensions; 72 | $this->sizeLimit = $sizeLimit; 73 | 74 | //$this->checkServerSettings(); 75 | 76 | if (isset($_GET['qqfile'])) { 77 | $this->file = new qqUploadedFileXhr(); 78 | } elseif (isset($_FILES['qqfile'])) { 79 | $this->file = new qqUploadedFileForm(); 80 | } else { 81 | $this->file = false; 82 | } 83 | } 84 | 85 | function checkServerSettings(){ 86 | $postSize = $this->toBytes(ini_get('post_max_size')); 87 | $uploadSize = $this->toBytes(ini_get('upload_max_filesize')); 88 | 89 | if ($postSize < $this->sizeLimit || $uploadSize < $this->sizeLimit){ 90 | $size = max(1, $this->sizeLimit / 1024 / 1024) . 'M'; 91 | die("{'error':'increase post_max_size and upload_max_filesize to $size'}"); 92 | } 93 | } 94 | 95 | function toBytes($str){ 96 | $val = trim($str); 97 | $last = strtolower($str[strlen($str)-1]); 98 | switch($last) { 99 | case 'g': $val *= 1024; 100 | case 'm': $val *= 1024; 101 | case 'k': $val *= 1024; 102 | } 103 | return $val; 104 | } 105 | 106 | /** 107 | * Returns array('success'=>true, 'file'=>$filename) or array('error'=>'error message') 108 | */ 109 | function handleUpload($uploadDirectory, $replaceOldFile = FALSE){ 110 | if (!is_writable($uploadDirectory)){ 111 | return array('error' => "Server error. Upload directory isn't writable."); 112 | } 113 | 114 | if (!$this->file){ 115 | return array('error' => 'No files were uploaded.'); 116 | } 117 | 118 | $size = $this->file->getSize(); 119 | 120 | if ($size == 0) { 121 | return array('error' => 'File is empty'); 122 | } 123 | 124 | if ($size > $this->sizeLimit) { 125 | return array('error' => 'File is too large'); 126 | } 127 | 128 | $pathinfo = pathinfo($this->file->getName()); 129 | $filename = strtolower($pathinfo['filename']); 130 | //$filename = md5(uniqid()); 131 | $ext = strtolower($pathinfo['extension']); 132 | 133 | if($this->allowedExtensions && !in_array(strtolower($ext), $this->allowedExtensions)){ 134 | $these = implode(', ', $this->allowedExtensions); 135 | return array('error' => 'File has an invalid extension, it should be one of '. $these . '.'); 136 | } 137 | 138 | if(!$replaceOldFile){ 139 | /// don't overwrite previous files that were uploaded 140 | while (file_exists($uploadDirectory . $filename . '.' . $ext)) { 141 | $filename .= rand(10, 99); 142 | } 143 | } 144 | $filename = sanitize_file_name($filename); 145 | 146 | if ($this->file->save($uploadDirectory . $filename . '.' . $ext)){ 147 | return array('success'=>true, 'file'=> $filename . '.' . $ext); 148 | } else { 149 | return array('error'=> 'Could not save uploaded file.' . 150 | 'The upload was cancelled, or server error encountered'); 151 | } 152 | 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /lib/class_bpfb_codec.php: -------------------------------------------------------------------------------- 1 | false, 19 | 'title' => false, 20 | 'image' => false, 21 | ), $atts)); 22 | if (!$url) return ''; 23 | 24 | $template = locate_template(array('link_tag_template.php')); 25 | if (empty($template)) $template = BPFB_PLUGIN_BASE_DIR . '/lib/forms/link_tag_template.php'; 26 | 27 | ob_start(); 28 | @include $template; 29 | $out = ob_get_clean(); 30 | return $out; 31 | } 32 | 33 | /** 34 | * Creates the proper shortcode tag based on the submitted data. 35 | */ 36 | function create_link_tag ($url, $title, $body='', $image='') { 37 | if (!$url) return ''; 38 | $title = $this->_escape_shortcode($title); 39 | $body = !empty($body) ? $this->_escape_shortcode($body) : $title; 40 | $title = esc_attr($title); 41 | $image = esc_url($image); 42 | $url = esc_url($url); 43 | return "[bpfb_link url='{$url}' title='{$title}' image='{$image}']{$body}[/bpfb_link]"; 44 | } 45 | 46 | /** 47 | * Escape shortcode-breaking characters. 48 | * 49 | * @param string $string String to process 50 | * 51 | * @return string 52 | */ 53 | private function _escape_shortcode ($string='') { 54 | if (empty($string)) return $string; 55 | 56 | $string = preg_replace('/' . preg_quote('[', '/') . '/', '[', $string); 57 | $string = preg_replace('/' . preg_quote(']', '/') . '/', ']', $string); 58 | 59 | return $string; 60 | } 61 | 62 | /** 63 | * Processes video-type shortcode and create proper markup. 64 | * Relies on `wp_oembed_get()` for markup rendering. 65 | */ 66 | function process_video_tag ($atts, $content) { 67 | return wp_oembed_get($content, array('width' => Bpfb_Data::get('oembed_width', 450))); 68 | } 69 | 70 | /** 71 | * Creates the proper shortcode tag based on the submitted data. 72 | */ 73 | function create_video_tag ($url) { 74 | if (!$url) return ''; 75 | $url = preg_match('/^https?:\/\//i', $url) ? $url : BPFB_PROTOCOL . $url; 76 | $url = esc_url($url); 77 | return "[bpfb_video]{$url}[/bpfb_video]"; 78 | } 79 | 80 | /** 81 | * Processes images-type shortcode and create proper markup. 82 | * Relies on ./forms/images_tag_template.php for markup rendering. 83 | */ 84 | function process_images_tag ($atts, $content) { 85 | $images = self::extract_images($content); 86 | //return var_export($images,1); 87 | $activity_id = bp_get_activity_id(); 88 | global $blog_id; 89 | $activity_blog_id = $blog_id; 90 | $use_thickbox = defined('BPFB_USE_THICKBOX') ? esc_attr(BPFB_USE_THICKBOX) : 'thickbox'; 91 | if ($activity_id) { 92 | $activity_blog_id = bp_activity_get_meta($activity_id, 'bpfb_blog_id'); 93 | } 94 | 95 | $template = locate_template(array('images_tag_template.php')); 96 | if (empty($template)) $template = BPFB_PLUGIN_BASE_DIR . '/lib/forms/images_tag_template.php'; 97 | 98 | ob_start(); 99 | @include $template; 100 | $out = ob_get_clean(); 101 | return $out; 102 | } 103 | 104 | /** 105 | * Creates the proper shortcode tag based on the submitted data. 106 | */ 107 | function create_images_tag ($imgs) { 108 | if (!$imgs) return ''; 109 | if (!is_array($imgs)) $imgs = array($imgs); 110 | return "[bpfb_images]\n" . join("\n", $imgs) . "\n[/bpfb_images]"; 111 | } 112 | 113 | /** 114 | * Wrap shortcode execution in a quick check. 115 | * 116 | * @param string $content Content to check for shortcode and process accordingly 117 | * 118 | * @return string 119 | */ 120 | public function do_shortcode ($content='') { 121 | if (false === strpos($content, '[bpfb_')) return $content; 122 | 123 | remove_filter('bp_get_activity_content_body', 'stripslashes_deep', 5); // Drop this because we'll be doing this right now 124 | $content = stripslashes_deep($content); // ... and process immediately, before allowing shortcode processing 125 | 126 | return do_shortcode($content); 127 | } 128 | 129 | /** 130 | * Registers shotcode processing procedures. 131 | */ 132 | public static function register () { 133 | $me = new BpfbCodec; 134 | add_shortcode('bpfb_link', array($me, 'process_link_tag')); 135 | add_shortcode('bpfb_video', array($me, 'process_video_tag')); 136 | add_shortcode('bpfb_images', array($me, 'process_images_tag')); 137 | 138 | // A fix for Ray's "oEmbed for BuddyPress" and similar plugins 139 | add_filter('bp_get_activity_content_body', array($me, 'do_shortcode'), 1); 140 | // RSS feed processing 141 | add_filter('bp_get_activity_feed_item_description', 'do_shortcode'); 142 | } 143 | 144 | /** 145 | * Checks whether we have an images list shortcode in content. 146 | * @param string $content String to check 147 | * @return boolean 148 | */ 149 | public static function has_images ($content) { 150 | return has_shortcode($content, 'bpfb_images'); 151 | } 152 | 153 | /** 154 | * Extracts images from shortcode content. 155 | * @param string $shortcode_content Shortcode contents 156 | * @return array 157 | */ 158 | public static function extract_images ($shortcode_content) { 159 | return explode("\n", trim(strip_tags($shortcode_content))); 160 | } 161 | } -------------------------------------------------------------------------------- /css/external/font/bpfb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | This is a custom SVG font generated by IcoMoon. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 25 | 29 | 30 | 40 | 44 | 48 | 51 | 52 | -------------------------------------------------------------------------------- /bpfb.php: -------------------------------------------------------------------------------- 1 | 232, 116 | 'name' => 'BuddyPress Activity Plus', 117 | 'screens' => array( 118 | 'settings_page_bpfb-settings', 119 | ), 120 | ); 121 | require_once BPFB_PLUGIN_BASE_DIR . '/lib/external/dash/wpmudev-dash-notification.php'; 122 | } 123 | require_once BPFB_PLUGIN_BASE_DIR . '/lib/class_bpfb_admin_pages.php'; 124 | Bpfb_Admin::serve(); 125 | } 126 | 127 | do_action('bpfb_init'); 128 | BpfbBinder::serve(); 129 | } 130 | // Only fire off if BP is actually loaded. 131 | add_action('bp_loaded', 'bpfb_plugin_init'); 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BuddyPress Activity+ 2 | 3 | **INACTIVE NOTICE: This plugin is unsupported by WPMUDEV, we've published it here for those technical types who might want to fork and maintain it for their needs.** 4 | 5 | ## Translations 6 | 7 | Translation files can be found at https://github.com/wpmudev/translations 8 | 9 | ## Fast powerful image, video and link sharing for BuddyPress. 10 | 11 | 12 | 13 | ![share-video-735x470](https://premium.wpmudev.org/wp-content/uploads/2011/05/share-video-735x470.jpg) 14 | 15 | Integrate easy access to video, image and link sharing. 16 | 17 | ### Easy Sharing 18 | 19 | Add a set of buttons that make it easy to share content from across the web. Simplify video embed, photo sharing and content linking. Preview embedded content before posting, add your own commentary and auto-pull titles, descriptions and thumbnails. There's no configuration needed – just activate the plugin and allow your users to start sharing. 20 | 21 | ![Styles-735x470](https://premium.wpmudev.org/wp-content/uploads/2011/05/Styles-735x470.jpg) 22 | 23 | Use a built-in style or create your own. 24 | 25 | ### Styles That Fit 26 | 27 | Choose one of the included button style that best fits your theme. Button designs include Legacy, Modern and rounded. Or follow the simple customization guide and craft buttons that perfectly fit your design aesthetic. 28 | 29 | ![Link-post-735x470](https://premium.wpmudev.org/wp-content/uploads/2011/05/Link-post-735x470.jpg) 30 | 31 | Autofill title and description and choose a thumbnail. 32 | 33 | ### Complete Control 34 | 35 | Configuration tools are super easy to use from toggle theme and alignment selection to oEmbed and thumbnail size setup. All the BuddyPress Activity + settings can be accessed from one simple settings page. 36 | 37 | ## Usage 38 | 39 | ### To Get Started: 40 | 41 | Start by reading [Installing Plugins](https://wpmudev.com/docs/using-wordpress/installing-wordpress-plugins/) section in our [comprehensive WordPress and WordPress Multisite Manual](https://premium.wpmudev.org/wpmu-manual/) if you are new to WordPress. 42 | 43 | ### To Install: 44 | 45 | 1.  Download the plugin file 46 | 47 | 2.  Unzip the file into a folder on your hard drive 48 | 49 | 3.  Upload the **_/_buddypress-activity-plus****_/_** folder and all its contents to the **/wp-content/plugins/** folder on your site 50 | 51 | 4.  Login to your admin panel for WordPress or Multisite and activate the plugin: 52 | 53 | * On regular WordPress installs - visit **Plugins** and **Activate** the plugin. 54 | * For WordPress Multisite installs - visit **Network Admin -> Plugins** and **Network Activate** the plugin. 55 | 56 | 5\. Once installed and activated, you will see a new menu item in your admin at Settings > Activity Plus. 57 | 58 | ### Configuring Settings 59 | 60 | Basic settings can be configured at Settings > Activity Plus. 61 | 62 | ![alt](https://premium.wpmudev.org/wp-content/uploads/2011/05/buddypress-activity-plus-settings.png) 63 | 64 | 1. Theme 65 | 2\. Alignment 66 | 3. oEmbed Width 67 | 4. Image thumbnail dimensions 68 | 69 | 1\. Select a _Theme_ to be used for the icons. 70 | 71 | 2\. Choose the _Alignment_, either Left or Right. This alignment setting will be used for the posted media. 72 | 73 | 3\. Specify the _oEmbed Width_, which will be the width of any media files such as videos. The height of the media will adjust to accommodate the width. 74 | 75 | 4\. Specify the _Image thumbnail dimensions_, both Width and Height, to be used for thumbnail images for the posted media. Now let's take a look at further customization options. 76 | 77 | ### Usage and Customization 78 | 79 | You can also use custom icons in your theme, simply use add_theme_support("bpfb_toolbar_icons"); in your functions.php, copy over the rules from css/bpfb_toolbar.css and edit to suit your needs. Alternatively, if you're OK with 32x32 icon sizes, you can just override the icons in your stylesheet using background property and !important. These are the IDs: #bpfb_addPhotos, #bpfb_addVideos, #bpfb_addLinks, #bpfb_addDocuments You can also set your preferred thumbnail size separately from your default thumbnail size settings, if you wish to do so. You can do that by adding this line to your wp-config.php: `define('BPFB_THUMBNAIL_IMAGE_SIZE', '200x200');` Where "200x200" are width and height (in that order), in pixels. Finally, be sure to verify your default sizes for embedded media. It's in Settings -> Media -> Embeds -> Maximum embed size There are a few additional constants that you can override in your _wp-config.php_ file to further customize how the plugin functions. Add the following to wp-config.php to override oEmbed width: `define('BPFB_OEMBED_WIDTH', 450, true);` Add the following to wp-config.php to increase/decrease the number of allowed images per activity item: `define('BPFB_IMAGE_LIMIT', 5, true);` Add the following to wp-config.php to open links in a new window: `define('BPFB_LINKS_TARGET', true);` 80 | 81 | ### User Experience 82 | 83 | Here are a few screenshots of what this plugin could look like on your site for your users (depending on your theme of course). 84 | 85 | ![Adding an image to an activity update.](https://premium.wpmudev.org/wp-content/uploads/2011/05/buddypress-activity-plus-add-image.png) 86 | 87 | Adding an image to an activity update. 88 | 89 | 90 | 91 | ![Image added to update.](https://premium.wpmudev.org/wp-content/uploads/2011/05/buddypress-activity-plus-image-added.png) 92 | 93 | Image added to update. 94 | 95 | 96 | 97 | ![Adding a video to an activity update.](https://premium.wpmudev.org/wp-content/uploads/2011/05/buddypress-activity-plus-add-video.png) 98 | 99 | Adding a video to an activity update. 100 | 101 | 102 | 103 | ![Video added to update.](https://premium.wpmudev.org/wp-content/uploads/2011/05/buddypress-activity-plus-video-added.png) 104 | 105 | Video added to update. 106 | 107 | 108 | 109 | ![Adding a link to an activity update.](https://premium.wpmudev.org/wp-content/uploads/2011/05/buddypress-activity-plus-add-link.png) 110 | 111 | Adding a link to an activity update. 112 | 113 | 114 | 115 | ![Link added to update.](https://premium.wpmudev.org/wp-content/uploads/2011/05/buddypress-activity-plus-link-added.png) 116 | 117 | Link added to update. 118 | 119 | ### Known Issues 120 | 121 | When using the plugin in combination with BuddyPress Media, a conflict can arise due to the activity stream upload, since both the plugins are trying to upload media through the activity stream. If you want to use the BuddyPress Activity Plus functionality, uncheck "Enable Activity Uploading" on the BuddyPress Media Settings Page. 122 | -------------------------------------------------------------------------------- /lib/class_bpfb_admin_pages.php: -------------------------------------------------------------------------------- 1 | _capability = bp_core_do_network_admin() 10 | ? 'manage_network_options' 11 | : 'manage_options' 12 | ; 13 | } 14 | 15 | public static function serve () { 16 | $me = new self; 17 | $me->_add_hooks(); 18 | } 19 | 20 | private function _add_hooks () { 21 | add_action(bp_core_admin_hook(), array($this, 'add_menu_page')); 22 | add_action('admin_enqueue_scripts', array($this, 'enqueue_dependencies')); 23 | } 24 | 25 | public function add_menu_page () { 26 | $hook = bp_core_do_network_admin() 27 | ? 'settings.php' 28 | : 'options-general.php' 29 | ; 30 | $this->_page_hook = add_submenu_page( 31 | $hook, 32 | __('BuddyPress Activity Plus', 'bpfb'), 33 | __('Activity Plus', 'bpfb'), 34 | $this->_capability, 35 | 'bpfb-settings', 36 | array($this, 'settings_page') 37 | ); 38 | 39 | $this->_save_settings(); 40 | } 41 | 42 | public function enqueue_dependencies ($hook) { 43 | if ($hook !== $this->_page_hook) return false; 44 | wp_enqueue_style('bpfb-admin', BPFB_PLUGIN_URL . '/css/admin.css'); 45 | } 46 | 47 | private function _save_settings () { 48 | if (empty($_POST['bpfb'])) return false; 49 | if (!current_user_can($this->_capability)) return false; 50 | if (!check_ajax_referer($this->_page_hook)) return false; 51 | 52 | $raw = stripslashes_deep($_POST['bpfb']); 53 | list($thumb_w,$thumb_h) = Bpfb_Data::get_thumbnail_size(true); 54 | $raw['thumbnail_size_height'] = !empty($raw['thumbnail_size_height']) && (int)$raw['thumbnail_size_height'] 55 | ? (int)$raw['thumbnail_size_height'] 56 | : $thumb_h 57 | ; 58 | $raw['thumbnail_size_width'] = !empty($raw['thumbnail_size_width']) && (int)$raw['thumbnail_size_width'] 59 | ? (int)$raw['thumbnail_size_width'] 60 | : $thumb_w 61 | ; 62 | $raw['oembed_width'] = !empty($raw['oembed_width']) && (int)$raw['oembed_width'] 63 | ? (int)$raw['oembed_width'] 64 | : Bpfb_Data::get('oembed_width') 65 | ; 66 | $raw['theme'] = !empty($raw['theme']) 67 | ? sanitize_html_class($raw['theme']) 68 | : '' 69 | ; 70 | $raw['cleanup_images'] = !empty($raw['cleanup_images']) 71 | ? (int)$raw['cleanup_images'] 72 | : false 73 | ; 74 | 75 | update_option('bpfb', $raw); 76 | wp_safe_redirect(add_query_arg(array('updated' => true))); 77 | } 78 | 79 | public function settings_page () { 80 | $theme = Bpfb_Data::get('theme'); 81 | list($thumb_w,$thumb_h) = Bpfb_Data::get_thumbnail_size(); 82 | $oembed_width = Bpfb_Data::get('oembed_width', 450); 83 | $alignment = Bpfb_Data::get('alignment', 'left'); 84 | $cleanup_images = Bpfb_Data::get('cleanup_images', false); 85 | ?> 86 |
87 | 88 |

89 |
90 | 91 |
92 | 93 | 94 | 95 |
96 |

97 |
98 | 99 | 100 |
101 | 102 | 107 | 112 | 117 |
118 |
119 | 120 | 124 | 128 |
129 |
130 | 131 | 132 |
133 | 134 | 135 |
136 | 137 | 138 |
139 |

BPFB_OEMBED_WIDTH define value (%s). Remove this define to enable this option.', 'bpfb'), BPFB_OEMBED_WIDTH); ?>

140 |
141 | 142 | 146 |
147 |
148 | 149 | 150 |
151 |

BPFB_THUMBNAIL_IMAGE_SIZE define value (%s). Remove this define to enable these options.', 'bpfb'), BPFB_THUMBNAIL_IMAGE_SIZE); ?>

152 |
153 | 154 | 158 | 162 |
163 |
164 | 165 | 169 |
170 |
171 | 172 |

173 | _page_hook); ?> 174 | 175 |

176 |
177 |
178 | _add_hooks(); 15 | } 16 | 17 | private function _add_hooks () { 18 | add_action('bpfb_add_ajax_hooks', array($this, 'add_ajax_hooks_handler')); 19 | add_action('bpfb_add_cssjs_hooks', array($this, 'add_cssjs_hooks_handler')); 20 | add_action('bpfb_code_before_save', array($this, 'code_before_save_handler')); 21 | add_action('bpfb_init', array($this, 'create_core_defines')); 22 | } 23 | 24 | /** 25 | * Registers required AJAX handlers. 26 | */ 27 | public function add_ajax_hooks_handler () { 28 | add_action('wp_ajax_bpfb_preview_document', array($this, 'ajax_preview_document')); 29 | add_action('wp_ajax_bpfb_remove_temp_documents', array($this, 'ajax_remove_temp_documents')); 30 | } 31 | 32 | public function add_js_globals () { 33 | printf( 34 | '', 35 | '"' . join('", "', array_map('trim', explode(',', BPFB_DOCUMENTS_ALLOWED_EXTENSIONS))) . '"' 36 | ); 37 | } 38 | 39 | /** 40 | * Injects required interface scripts. 41 | */ 42 | public function add_cssjs_hooks_handler () { 43 | if (!defined('BP_GROUP_DOCUMENTS_IS_INSTALLED') || !BP_GROUP_DOCUMENTS_IS_INSTALLED) return false; 44 | 45 | add_action('wp_print_scripts', array($this, 'add_js_globals')); 46 | wp_enqueue_script('bpfb_group_documents', BPFB_PLUGIN_URL . '/js/bpfb_group_documents.js', array('bpfb_interface_script')); 47 | wp_localize_script('bpfb_group_documents', 'l10nBpfbDocs', array( 48 | 'add_documents' => __('Add documents', 'bpfb'), 49 | 'no_group_selected' => __('Please select a group to upload to', 'bpfb'), 50 | )); 51 | } 52 | 53 | /** 54 | * Handles document upload preview 55 | */ 56 | public function ajax_preview_document () { 57 | $dir = BPFB_PLUGIN_BASE_DIR . '/img/'; 58 | if (!class_exists('qqFileUploader')) require_once(BPFB_PLUGIN_BASE_DIR . '/lib/external/file_uploader.php'); 59 | $uploader = new qqFileUploader(array_map('trim', explode(',', BPFB_DOCUMENTS_ALLOWED_EXTENSIONS))); 60 | $result = $uploader->handleUpload(BPFB_TEMP_IMAGE_DIR); 61 | 62 | if ($result['file']) { 63 | $doc_obj = new BP_Group_Documents(); 64 | $doc_obj->file = $result['file']; 65 | $result['icon'] = $doc_obj->get_icon(); 66 | } 67 | echo htmlspecialchars(json_encode($result), ENT_NOQUOTES); 68 | exit(); 69 | } 70 | 71 | /** 72 | * Checks upload permissions. 73 | * Adapted from Group Documents plugin. 74 | */ 75 | public function allowed ($group=false) { 76 | if (!$group) return false; 77 | 78 | $user = wp_get_current_user(); 79 | $moderator_of = BP_Groups_Member::get_is_admin_of($user->ID) + BP_Groups_Member::get_is_mod_of($user->ID); 80 | $moderator_of = (is_array($moderator_of) && isset($moderator_of['groups'])) ? $moderator_of['groups'] : false; 81 | 82 | $is_mod = false; 83 | foreach ($moderator_of as $gm) { 84 | if ($gm->id == $group->id) { 85 | $is_mod = true; break; 86 | } 87 | } 88 | 89 | switch (get_option( 'bp_group_documents_upload_permission')) { 90 | case 'mods_decide': 91 | switch (groups_get_groupmeta( $group->id, 'group_documents_upload_permission')) { 92 | case 'mods_only': 93 | if ($is_mod) return true; 94 | break; 95 | case 'members': 96 | default: 97 | if (groups_is_user_member($user->ID, $group->id)) return true; 98 | break; 99 | } 100 | break; 101 | case 'mods_only': 102 | if ($is_mod) return true; 103 | break; 104 | case 'members': 105 | default: 106 | if (groups_is_user_member($user->ID, $group->id)) return true; 107 | break; 108 | } 109 | return false; 110 | } 111 | 112 | /** 113 | * Handles save request. 114 | */ 115 | public function code_before_save_handler ($code) { 116 | $data = !empty($_POST['data']) ? stripslashes_deep($_POST['data']) : array(); 117 | if (!empty($data['bpfb_documents'])) { 118 | $docs = $this->move($data['bpfb_documents']); 119 | $code = $this->create_documents_tag($docs); 120 | } 121 | return $code; 122 | } 123 | 124 | /** 125 | * Clears up the temporary documents storage. 126 | */ 127 | public function ajax_remove_temp_documents () { 128 | header('Content-type: application/json'); 129 | parse_str($_POST['data'], $data); 130 | $data = is_array($data) ? $data : array('bpfb_documents'=>array()); 131 | foreach ($data['bpfb_documents'] as $file) { 132 | $path = BpfbBinder::resolve_temp_path($file); 133 | if (!empty($path)) @unlink($path); 134 | } 135 | echo json_encode(array('status'=>'ok')); 136 | exit(); 137 | } 138 | 139 | /** 140 | * Moves the documents to a place recognized by Group Documents plugin 141 | * and saves them. 142 | */ 143 | public function move ($docs) { 144 | if (!$docs) return false; 145 | if (!is_array($docs)) $docs = array($docs); 146 | 147 | if (!(int)@$_POST['group_id']) return false; 148 | 149 | $group = new BP_Groups_Group((int)@$_POST['group_id']); 150 | if (!$this->allowed($group)) return false; 151 | 152 | global $bp; 153 | $ret = array(); 154 | 155 | // Construct the needed data 156 | $user = wp_get_current_user(); 157 | $data = array ( 158 | 'user_id' => $user->ID, 159 | 'group_id' => (int)@$_POST['group_id'], 160 | 'created_ts' => time(), 161 | 'modified_ts' => time(), 162 | 'file' => '', 163 | 'name' => '', 164 | 'description' => @$_POST['content'], 165 | ); 166 | 167 | foreach ($docs as $doc) { 168 | $doc_obj = new BP_Group_Documents(); 169 | foreach ($data as $key=>$val) { 170 | $doc_obj->$key = $val; 171 | } 172 | $doc_obj->name = $doc; 173 | $doc_obj->file = apply_filters('bp_group_documents_filename_in', $doc); 174 | 175 | $tmp_doc = realpath(BPFB_TEMP_IMAGE_DIR . $doc); 176 | $new_doc = $doc_obj->get_path(0,1); 177 | 178 | if (@rename($tmp_doc, $new_doc) && $doc_obj->save(false)) { 179 | $ret[] = $doc_obj; 180 | } 181 | } 182 | 183 | return $ret; 184 | } 185 | 186 | /** 187 | * Creates the activity info message. 188 | * No shortcode, just renders the appropriate HTML. 189 | */ 190 | public function create_documents_tag ($docs) { 191 | if (!$docs || !is_array($docs)) return false; 192 | 193 | global $bp; 194 | $uploaded = array(); 195 | $group = false; 196 | foreach ($docs as $doc) { 197 | if (!$group) $group = new BP_Groups_Group($doc->group_id); 198 | $uploaded[] = '' . esc_attr($doc->name) . ''; 199 | } 200 | return sprintf( 201 | __('%s uploaded new file(s): %s to %s', 'bpfb'), 202 | bp_core_get_userlink($bp->loggedin_user->id), 203 | join(', ', $uploaded), 204 | '' . bp_get_group_name($group) . '' 205 | ); 206 | } 207 | 208 | public function create_core_defines () { 209 | if (!defined('BPFB_DOCUMENTS_ALLOWED_EXTENSIONS')) { 210 | $exts = get_option('bp_group_documents_valid_file_formats'); 211 | if ($exts) { 212 | define('BPFB_DOCUMENTS_ALLOWED_EXTENSIONS', $exts); 213 | } else { 214 | /** 215 | * This define is a list of allowed file extensions. 216 | * It can be overriden in wp-config.php 217 | */ 218 | define( 219 | 'BPFB_DOCUMENTS_ALLOWED_EXTENSIONS', 220 | 'adp, as, avi, bash, bz, bz2, c, cf, cpp, cs, css, deb, doc, docx, eps, exe, fh, fl, gif, gz, htm, html, iso, java, jpeg, jpg, json, m4a, mov, mdb, mp3, mpeg, msp, ods, odt, ogg, perl, pdf, php, png, ppt, pps, pptx, ps, rb, rtf, sh, sql, swf, tar, txt, wav, xls, xlsx, xml, zip' 221 | ); 222 | } 223 | } 224 | } 225 | 226 | } 227 | Bpfb_Documents::serve(); -------------------------------------------------------------------------------- /css/bpfb_toolbar.css: -------------------------------------------------------------------------------- 1 | .bpfb_toolbar_container, .bpfb_controls_container { 2 | clear: left; 3 | padding-top: 5px; 4 | } 5 | .bpfb-alignment-right .bpfb_toolbar_container, .bpfb-alignment-right .bpfb_controls_container { 6 | clear: right; 7 | } 8 | 9 | .bpfb_toolbarItem { 10 | border: none; 11 | text-decoration: none; 12 | display: block; 13 | float: left; 14 | height: 32px; 15 | width: 32px; 16 | margin-right: 1ex; 17 | overflow: hidden; 18 | } 19 | .bpfb-alignment-right .bpfb_toolbarItem { 20 | float: right; 21 | } 22 | 23 | .bpfb_toolbarItem span { 24 | display: block; 25 | position: absolute; 26 | left: -12000000000px; 27 | display: none; 28 | } 29 | 30 | #bpfb_addPhotos { 31 | background: url(../img/system/camera.png) left top no-repeat; 32 | } 33 | #bpfb_addVideos { 34 | background: url(../img/system/film.png) left top no-repeat; 35 | } 36 | #bpfb_addLinks { 37 | background: url(../img/system/link.png) left top no-repeat; 38 | } 39 | #bpfb_addDocuments { 40 | background: url(../img/system/document.png) left top no-repeat; 41 | } 42 | 43 | 44 | /* --- */ 45 | 46 | 47 | /* ----- New interface ----- */ 48 | .bpfb_actions_container.bpfb-theme-new .bpfb_toolbarItem, 49 | .bpfb_actions_container.bpfb-theme-new .bpfb_toolbarItem:visited, 50 | .bpfb_actions_container.bpfb-theme-new .bpfb_toolbarItem:hover 51 | { 52 | color: #5FB3C9; 53 | } 54 | .bpfb_actions_container.bpfb-theme-new .bpfb_toolbarItem:active, 55 | .bpfb_actions_container.bpfb-theme-new .bpfb_toolbarItem.bpfb_active { 56 | color: #F33; 57 | } 58 | .bpfb_actions_container.bpfb-theme-new #bpfb_addPhotos, 59 | .bpfb_actions_container.bpfb-theme-new #bpfb_addVideos, 60 | .bpfb_actions_container.bpfb-theme-new #bpfb_addLinks, 61 | .bpfb_actions_container.bpfb-theme-new #bpfb_addDocuments { 62 | background: none; 63 | font-family:'bpfb'; 64 | font-size: 24px; 65 | line-height: 32px; 66 | font-weight:normal; 67 | margin-right: .5em; 68 | text-align: center; 69 | } 70 | .bpfb_actions_container.bpfb-theme-new #bpfb_addPhotos:before {content:'I';} 71 | .bpfb_actions_container.bpfb-theme-new #bpfb_addVideos:before {content:'V';} 72 | .bpfb_actions_container.bpfb-theme-new #bpfb_addLinks:before {content:'a';} 73 | .bpfb_actions_container.bpfb-theme-new #bpfb_addDocuments:before {content:'D';} 74 | 75 | /* primary button color scheme */ 76 | .bpfb_actions_container.bpfb-theme-round .button-primary.bpfb_primary_button, 77 | .bpfb_actions_container.bpfb-theme-round .qq-upload-button 78 | { 79 | color: #FFFFFF; 80 | background: #5FB3C9 !important; 81 | } 82 | 83 | /* ----- Waiting animation / requires CSS3 transforms ----- */ 84 | .bpfb_actions_container.bpfb-theme-new .bpfb_waiting { 85 | background: none; 86 | font-family:'bpfb'; 87 | font-size: 24px; 88 | font-weight:normal; 89 | text-align: center; 90 | } 91 | .bpfb_actions_container.bpfb-theme-new .bpfb_waiting:after { 92 | display: block; 93 | content: "\2e"; 94 | height: 24px; 95 | width: 24px; 96 | line-height: 24px; 97 | margin: 0 auto; 98 | -webkit-animation:bpfb_spin 1s infinite steps(8); 99 | -moz-animation:bpfb_spin 1s infinite steps(8); 100 | animation:bpfb_spin 1s infinite steps(8); 101 | } 102 | @-moz-keyframes bpfb_spin { 0% {-moz-transform: rotate(0);} 100% { -moz-transform: rotate(360deg); } } 103 | @-webkit-keyframes bpfb_spin { 0% {-webkit-transform: rotate(0);} 100% { -webkit-transform: rotate(360deg); } } 104 | @keyframes bpfb_spin { 0% {transform: rotate(0);} 100% { transform: rotate(360deg); } } 105 | 106 | 107 | .bpfb_actions_container.bpfb-theme-new div.bpfb_thumbnail_chooser img { 108 | display: none; 109 | } 110 | .bpfb_actions_container.bpfb-theme-new span.bpfb_thumbnail_chooser_label { 111 | padding: 0; 112 | } 113 | 114 | .bpfb_actions_container.bpfb-theme-new .bpfb_thumbnail_chooser { 115 | font-size: 12px; 116 | line-height: 16px; 117 | } 118 | .bpfb_actions_container.bpfb-theme-new .bpfb_thumbnail_chooser .bpfb_left:before { 119 | display: inline-block; 120 | font-size: 16px; 121 | height: 16px; 122 | width: 24px; 123 | font-family:'bpfb'; 124 | content: "L"; 125 | cursor: pointer; 126 | } 127 | .bpfb_actions_container.bpfb-theme-new .bpfb_thumbnail_chooser .bpfb_right:after { 128 | display: inline-block; 129 | font-size: 16px; 130 | height: 16px; 131 | width: 24px; 132 | padding-left: 1em; 133 | font-family:'bpfb'; 134 | content: "R"; 135 | cursor: pointer; 136 | } 137 | 138 | 139 | /* ----- Round interface ----- */ 140 | .bpfb_actions_container.bpfb-theme-round .bpfb_toolbarItem { 141 | line-height: 32px; 142 | border-radius: 32px; 143 | } 144 | .bpfb_actions_container.bpfb-theme-round #bpfb_addPhotos, 145 | .bpfb_actions_container.bpfb-theme-round #bpfb_addVideos, 146 | .bpfb_actions_container.bpfb-theme-round #bpfb_addLinks, 147 | .bpfb_actions_container.bpfb-theme-round #bpfb_addDocuments { 148 | font-family:'bpfb'; 149 | font-size: 14px; 150 | font-weight: normal; 151 | margin-right: .5em; 152 | text-align: center; 153 | border: 1px solid #444; 154 | } 155 | .bpfb_actions_container.bpfb-theme-round #bpfb_addPhotos:before {content:'I';} 156 | .bpfb_actions_container.bpfb-theme-round #bpfb_addVideos:before {content:'V';} 157 | .bpfb_actions_container.bpfb-theme-round #bpfb_addLinks:before {content:'a';} 158 | .bpfb_actions_container.bpfb-theme-round #bpfb_addDocuments:before {content:'D';} 159 | 160 | .bpfb_actions_container.bpfb-theme-round .bpfb_toolbarItem, 161 | .bpfb_actions_container.bpfb-theme-round .bpfb_toolbarItem:visited, 162 | .bpfb_actions_container.bpfb-theme-round .bpfb_toolbarItem:hover 163 | { 164 | color: #eee; 165 | background: #555 !important; 166 | } 167 | .bpfb_actions_container.bpfb-theme-round .bpfb_toolbarItem:active, 168 | .bpfb_actions_container.bpfb-theme-round .bpfb_toolbarItem.bpfb_active { 169 | color: #333; 170 | background: #eee !important; 171 | border: none !important; 172 | font-size: 24px !important; 173 | } 174 | /* primary button color scheme */ 175 | .bpfb_actions_container.bpfb-theme-round .button-primary.bpfb_primary_button, 176 | .bpfb_actions_container.bpfb-theme-round .qq-upload-button 177 | { 178 | color: #eee; 179 | background: #555 !important; 180 | } 181 | 182 | 183 | /* ----- Waiting animation / requires CSS3 transforms ----- */ 184 | .bpfb_actions_container.bpfb-theme-round .bpfb_waiting { 185 | background: none; 186 | font-family:'bpfb'; 187 | font-size: 24px; 188 | font-weight:normal; 189 | text-align: center; 190 | } 191 | .bpfb_actions_container.bpfb-theme-round .bpfb_waiting:after { 192 | display: block; 193 | content: "\2e"; 194 | height: 24px; 195 | width: 24px; 196 | line-height: 24px; 197 | margin: 0 auto; 198 | -webkit-animation:bpfb_spin 1s infinite steps(8); 199 | -moz-animation:bpfb_spin 1s infinite steps(8); 200 | animation:bpfb_spin 1s infinite steps(8); 201 | } 202 | @-moz-keyframes bpfb_spin { 0% {-moz-transform: rotate(0);} 100% { -moz-transform: rotate(360deg); } } 203 | @-webkit-keyframes bpfb_spin { 0% {-webkit-transform: rotate(0);} 100% { -webkit-transform: rotate(360deg); } } 204 | @keyframes bpfb_spin { 0% {transform: rotate(0);} 100% { transform: rotate(360deg); } } 205 | 206 | 207 | .bpfb_actions_container.bpfb-theme-round div.bpfb_thumbnail_chooser img { 208 | display: none; 209 | } 210 | .bpfb_actions_container.bpfb-theme-round span.bpfb_thumbnail_chooser_label { 211 | padding: 0; 212 | } 213 | 214 | .bpfb_actions_container.bpfb-theme-round .bpfb_thumbnail_chooser { 215 | font-size: 12px; 216 | line-height: 16px; 217 | } 218 | .bpfb_actions_container.bpfb-theme-round .bpfb_thumbnail_chooser .bpfb_left:before { 219 | display: inline-block; 220 | font-size: 16px; 221 | height: 16px; 222 | width: 24px; 223 | font-family:'bpfb'; 224 | content: "L"; 225 | cursor: pointer; 226 | } 227 | .bpfb_actions_container.bpfb-theme-round .bpfb_thumbnail_chooser .bpfb_right:after { 228 | display: inline-block; 229 | font-size: 16px; 230 | height: 16px; 231 | width: 24px; 232 | padding-left: 1em; 233 | font-family:'bpfb'; 234 | content: "R"; 235 | cursor: pointer; 236 | } -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 6 | 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | Preamble 11 | 12 | The licenses for most software are designed to take away your 13 | freedom to share and change it. By contrast, the GNU General Public 14 | License is intended to guarantee your freedom to share and change free 15 | software--to make sure the software is free for all its users. This 16 | General Public License applies to most of the Free Software 17 | Foundation's software and to any other program whose authors commit to 18 | using it. (Some other Free Software Foundation software is covered by 19 | the GNU Library General Public License instead.) You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | this service if you wish), that you receive source code or can get it 26 | if you want it, that you can change the software or use pieces of it 27 | in new free programs; and that you know you can do these things. 28 | 29 | To protect your rights, we need to make restrictions that forbid 30 | anyone to deny you these rights or to ask you to surrender the rights. 31 | These restrictions translate to certain responsibilities for you if you 32 | distribute copies of the software, or if you modify it. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must give the recipients all the rights that 36 | you have. You must make sure that they, too, receive or can get the 37 | source code. And you must show them these terms so they know their 38 | rights. 39 | 40 | We protect your rights with two steps: (1) copyright the software, and 41 | (2) offer you this license which gives you legal permission to copy, 42 | distribute and/or modify the software. 43 | 44 | Also, for each author's protection and ours, we want to make certain 45 | that everyone understands that there is no warranty for this free 46 | software. If the software is modified by someone else and passed on, we 47 | want its recipients to know that what they have is not the original, so 48 | that any problems introduced by others will not reflect on the original 49 | authors' reputations. 50 | 51 | Finally, any free program is threatened constantly by software 52 | patents. We wish to avoid the danger that redistributors of a free 53 | program will individually obtain patent licenses, in effect making the 54 | program proprietary. To prevent this, we have made it clear that any 55 | patent must be licensed for everyone's free use or not licensed at all. 56 | 57 | The precise terms and conditions for copying, distribution and 58 | modification follow. 59 | 60 | GNU GENERAL PUBLIC LICENSE 61 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 62 | 63 | 0. This License applies to any program or other work which contains 64 | a notice placed by the copyright holder saying it may be distributed 65 | under the terms of this General Public License. The "Program", below, 66 | refers to any such program or work, and a "work based on the Program" 67 | means either the Program or any derivative work under copyright law: 68 | that is to say, a work containing the Program or a portion of it, 69 | either verbatim or with modifications and/or translated into another 70 | language. (Hereinafter, translation is included without limitation in 71 | the term "modification".) Each licensee is addressed as "you". 72 | 73 | Activities other than copying, distribution and modification are not 74 | covered by this License; they are outside its scope. The act of 75 | running the Program is not restricted, and the output from the Program 76 | is covered only if its contents constitute a work based on the 77 | Program (independent of having been made by running the Program). 78 | Whether that is true depends on what the Program does. 79 | 80 | 1. You may copy and distribute verbatim copies of the Program's 81 | source code as you receive it, in any medium, provided that you 82 | conspicuously and appropriately publish on each copy an appropriate 83 | copyright notice and disclaimer of warranty; keep intact all the 84 | notices that refer to this License and to the absence of any warranty; 85 | and give any other recipients of the Program a copy of this License 86 | along with the Program. 87 | 88 | You may charge a fee for the physical act of transferring a copy, and 89 | you may at your option offer warranty protection in exchange for a fee. 90 | 91 | 2. You may modify your copy or copies of the Program or any portion 92 | of it, thus forming a work based on the Program, and copy and 93 | distribute such modifications or work under the terms of Section 1 94 | above, provided that you also meet all of these conditions: 95 | 96 | a) You must cause the modified files to carry prominent notices 97 | stating that you changed the files and the date of any change. 98 | 99 | b) You must cause any work that you distribute or publish, that in 100 | whole or in part contains or is derived from the Program or any 101 | part thereof, to be licensed as a whole at no charge to all third 102 | parties under the terms of this License. 103 | 104 | c) If the modified program normally reads commands interactively 105 | when run, you must cause it, when started running for such 106 | interactive use in the most ordinary way, to print or display an 107 | announcement including an appropriate copyright notice and a 108 | notice that there is no warranty (or else, saying that you provide 109 | a warranty) and that users may redistribute the program under 110 | these conditions, and telling the user how to view a copy of this 111 | License. (Exception: if the Program itself is interactive but 112 | does not normally print such an announcement, your work based on 113 | the Program is not required to print an announcement.) 114 | 115 | These requirements apply to the modified work as a whole. If 116 | identifiable sections of that work are not derived from the Program, 117 | and can be reasonably considered independent and separate works in 118 | themselves, then this License, and its terms, do not apply to those 119 | sections when you distribute them as separate works. But when you 120 | distribute the same sections as part of a whole which is a work based 121 | on the Program, the distribution of the whole must be on the terms of 122 | this License, whose permissions for other licensees extend to the 123 | entire whole, and thus to each and every part regardless of who wrote it. 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | -------------------------------------------------------------------------------- /js/bpfb_interface.js: -------------------------------------------------------------------------------- 1 | var _bpfbActiveHandler = false; 2 | 3 | (function($){ 4 | $(function() { 5 | 6 | var $form; 7 | var $text; 8 | var $textContainer; 9 | 10 | 11 | /** 12 | * Video insertion/preview handler. 13 | */ 14 | var BpfbVideoHandler = function () { 15 | $container = $(".bpfb_controls_container"); 16 | 17 | var resize = function () { 18 | $('#bpfb_video_url').width($container.width()); 19 | }; 20 | 21 | var createMarkup = function () { 22 | var html = '' + 23 | ''; 24 | $container.empty().append(html); 25 | 26 | $(window).off("resize.bpfb").on("resize.bpfb", resize); 27 | resize(); 28 | $('#bpfb_video_url').focus(function () { 29 | $(this) 30 | .select() 31 | .addClass('changed') 32 | ; 33 | }); 34 | 35 | $('#bpfb_video_url').keypress(function (e) { 36 | if (13 != e.which) return true; 37 | createVideoPreview(); 38 | return false; 39 | }); 40 | $('#bpfb_video_url').change(createVideoPreview); 41 | $('#bpfb_video_url_preview').click(createVideoPreview); 42 | }; 43 | 44 | var createVideoPreview = function () { 45 | var url = $('#bpfb_video_url').val(); 46 | if (!url) return false; 47 | $('.bpfb_preview_container').html('
'); 48 | $.post(ajaxurl, {"action":"bpfb_preview_video", "data":url}, function (data) { 49 | $('.bpfb_preview_container').empty().html(data); 50 | $('.bpfb_action_container').html( 51 | '

' + 52 | '

' 53 | ); 54 | $("#bpfb_cancel_action").hide(); 55 | }); 56 | }; 57 | 58 | var processForSave = function () { 59 | return { 60 | "bpfb_video_url": $("#bpfb_video_url").val() 61 | }; 62 | }; 63 | 64 | var init = function () { 65 | $('#aw-whats-new-submit').hide(); 66 | createMarkup(); 67 | }; 68 | 69 | var destroy = function () { 70 | $container.empty(); 71 | $('.bpfb_preview_container').empty(); 72 | $('.bpfb_action_container').empty(); 73 | $('#aw-whats-new-submit').show(); 74 | $(window).off("resize.bpfb"); 75 | }; 76 | 77 | init (); 78 | 79 | return {"destroy": destroy, "get": processForSave}; 80 | }; 81 | 82 | 83 | /** 84 | * Link insertion/preview handler. 85 | */ 86 | var BpfbLinkHandler = function () { 87 | $container = $(".bpfb_controls_container"); 88 | 89 | var resize = function () { 90 | $('#bpfb_link_preview_url').width($container.width()); 91 | }; 92 | 93 | var createMarkup = function () { 94 | var html = '' + 95 | ''; 96 | $container.empty().append(html); 97 | 98 | $(window).off("resize.bpfb").on("resize.bpfb", resize); 99 | resize(); 100 | $('#bpfb_link_preview_url').focus(function () { 101 | $(this) 102 | .select() 103 | .addClass('changed') 104 | ; 105 | }); 106 | 107 | $('#bpfb_link_preview_url').keypress(function (e) { 108 | if (13 != e.which) return true; 109 | createLinkPreview(); 110 | return false; 111 | }); 112 | $('#bpfb_link_preview_url').change(createLinkPreview); 113 | $('#bpfb_link_url_preview').click(createLinkPreview); 114 | }; 115 | 116 | var createPreviewMarkup = function (data) { 117 | if (!data.url) { 118 | $('.bpfb_preview_container').empty().html(data.title); 119 | return false; 120 | } 121 | var imgs = ''; 122 | $.each(data.images, function(idx, img) { 123 | if (!img) return true; 124 | var url = img.match(/^http/) ? img : data.url + '/' + img; 125 | imgs += ''; 126 | }); 127 | var html = '' + 128 | '' + 129 | '' + 135 | '' + 149 | '' + 150 | '
' + 130 | '' + 134 | '' + 136 | '' + 137 | '' + 138 | '' + 139 | '' + 140 | '' + 141 | '' + 142 | '
' + 143 | ' ' + 144 | '' + l10nBpfb.choose_thumbnail + '' + 145 | ' ' + 146 | '
' + 147 | '
' + 148 | '
'; 151 | $('.bpfb_preview_container').empty().html(html); 152 | $('.bpfb_action_container').html( 153 | '

' + 154 | '

' 155 | ); 156 | $("#bpfb_cancel_action").hide(); 157 | 158 | $('img.bpfb_link_preview_image').hide(); 159 | $('img.bpfb_link_preview_image').first().show(); 160 | $('input[name="bpfb_link_img"]').val($('img.bpfb_link_preview_image').first().attr('src')); 161 | 162 | //$('.bpfb_thumbnail_chooser_left').click(function () { 163 | $('.bpfb_thumbnail_chooser .bpfb_left').click(function () { 164 | var $cur = $('img.bpfb_link_preview_image:visible'); 165 | var $prev = $cur.prev('.bpfb_link_preview_image'); 166 | if ($prev.length) { 167 | $cur.hide(); 168 | $prev 169 | .width($('.bpfb_link_preview_container').width()) 170 | .show(); 171 | $('input[name="bpfb_link_img"]').val($prev.attr('src')); 172 | } 173 | return false; 174 | }); 175 | //$('.bpfb_thumbnail_chooser_right').click(function () { 176 | $('.bpfb_thumbnail_chooser .bpfb_right').click(function () { 177 | var $cur = $('img.bpfb_link_preview_image:visible'); 178 | var $next = $cur.next('.bpfb_link_preview_image'); 179 | if ($next.length) { 180 | $cur.hide(); 181 | $next 182 | .width($('.bpfb_link_preview_container').width()) 183 | .show(); 184 | $('input[name="bpfb_link_img"]').val($next.attr('src')); 185 | } 186 | return false; 187 | }); 188 | $("#bpfb_link_no_thumbnail").click(function () { 189 | if ($("#bpfb_link_no_thumbnail").is(":checked")) { 190 | $('img.bpfb_link_preview_image:visible').hide(); 191 | $('input[name="bpfb_link_img"]').val(''); 192 | $(".bpfb_left, .bpfb_right, .bpfb_thumbnail_chooser_label").hide(); 193 | } else { 194 | var $img = $('img.bpfb_link_preview_image:first'); 195 | $img.show(); 196 | $(".bpfb_left, .bpfb_right, .bpfb_thumbnail_chooser_label").show(); 197 | $('input[name="bpfb_link_img"]').val($img.attr('src')); 198 | } 199 | 200 | }); 201 | }; 202 | 203 | var createLinkPreview = function () { 204 | var url = $('#bpfb_link_preview_url').val(); 205 | if (!url) return false; 206 | $('.bpfb_preview_container').html('
'); 207 | $.post(ajaxurl, {"action":"bpfb_preview_link", "data":url}, function (data) { 208 | createPreviewMarkup(data); 209 | }); 210 | }; 211 | 212 | var processForSave = function () { 213 | return { 214 | "bpfb_link_url": $('input[name="bpfb_link_url"]').val(), 215 | "bpfb_link_image": $('input[name="bpfb_link_img"]').val(), 216 | "bpfb_link_title": $('input[name="bpfb_link_title"]').val(), 217 | "bpfb_link_body": $('input[name="bpfb_link_body"]').val() 218 | }; 219 | }; 220 | 221 | var init = function () { 222 | $('#aw-whats-new-submit').hide(); 223 | createMarkup(); 224 | }; 225 | 226 | var destroy = function () { 227 | $container.empty(); 228 | $('.bpfb_preview_container').empty(); 229 | $('.bpfb_action_container').empty(); 230 | $('#aw-whats-new-submit').show(); 231 | $(window).off("resize.bpfb"); 232 | }; 233 | 234 | init (); 235 | 236 | return {"destroy": destroy, "get": processForSave}; 237 | }; 238 | 239 | 240 | /** 241 | * Photos insertion/preview handler. 242 | */ 243 | var BpfbPhotoHandler = function () { 244 | $container = $(".bpfb_controls_container"); 245 | 246 | var createMarkup = function () { 247 | var html = '
' + 248 | '' + 249 | '
' + 250 | ''; 251 | $container.append(html); 252 | 253 | var uploader = new qq.FileUploader({ 254 | "element": $('#bpfb_tmp_photo')[0], 255 | "listElement": $('#bpfb_tmp_photo_list')[0], 256 | "allowedExtensions": ['jpg', 'jpeg', 'png', 'gif'], 257 | "action": ajaxurl, 258 | "params": { 259 | "action": "bpfb_preview_photo" 260 | }, 261 | "onSubmit": function (id) { 262 | if (!parseInt(l10nBpfb._max_images, 10)) return true; // Skip check 263 | id = parseInt(id, 10); 264 | if (!id) id = $("img.bpfb_preview_photo_item").length; 265 | if (!id) return true; 266 | if (id < parseInt(l10nBpfb._max_images, 10)) return true; 267 | if (!$("#bpfb-too_many_photos").length) $("#bpfb_tmp_photo").append( 268 | '

' + l10nBpfb.images_limit_exceeded + '

' 269 | ); 270 | return false; 271 | }, 272 | "onComplete": createPhotoPreview, 273 | template: '
' + 274 | '
' + l10nBpfb.drop_files + '
' + 275 | '
' + l10nBpfb.upload_file + '
' + 276 | '
    ' + 277 | '
    ' 278 | }); 279 | 280 | $("#bpfb_remote_image_preview").hide(); 281 | $("#bpfb_tmp_photo").click(function () { 282 | if ($("#bpfb_add_remote_image").is(":visible")) $("#bpfb_add_remote_image").hide(); 283 | }); 284 | $("#bpfb_add_remote_image").click(function () { 285 | if (!$("#bpfb_remote_image_preview").is(":visible")) $("#bpfb_remote_image_preview").show(); 286 | if ($("#bpfb_tmp_photo").is(":visible")) $("#bpfb_tmp_photo").hide(); 287 | $("#bpfb_add_remote_image").val(l10nBpfb.add_another_remote_image); 288 | $("#bpfb_remote_image_container").append( 289 | '
    ' 290 | ); 291 | $("#bpfb_remote_image_container .bpfb_remote_image").width($container.width()); 292 | }); 293 | $(document).on('change', "#bpfb_remote_image_container .bpfb_remote_image", createRemoteImagePreview); 294 | $("#bpfb_remote_image_preview").click(createRemoteImagePreview); 295 | }; 296 | 297 | var createRemoteImagePreview = function () { 298 | var imgs = []; 299 | $("#bpfb_remote_image_container .bpfb_remote_image").each(function () { 300 | imgs[imgs.length] = $(this).val(); 301 | }); 302 | $.post(ajaxurl, {"action":"bpfb_preview_remote_image", "data":imgs}, function (data) { 303 | var html = ''; 304 | $.each(data, function() { 305 | html += '' + 306 | ''; 307 | }); 308 | $('.bpfb_preview_container').html(html); 309 | }); 310 | $('.bpfb_action_container').html( 311 | '

    ' + 312 | '

    ' 313 | ); 314 | $("#bpfb_cancel_action").hide(); 315 | }; 316 | 317 | var createPhotoPreview = function (id, fileName, resp) { 318 | if ("error" in resp) return false; 319 | var html = '' + 320 | ''; 321 | $('.bpfb_preview_container').append(html); 322 | $('.bpfb_action_container').html( 323 | '

    ' + 324 | '

    ' 325 | ); 326 | $("#bpfb_cancel_action").hide(); 327 | }; 328 | 329 | var removeTempImages = function (rti_callback) { 330 | var $imgs = $('input.bpfb_photos_to_add'); 331 | if (!$imgs.length) return rti_callback(); 332 | $.post(ajaxurl, {"action":"bpfb_remove_temp_images", "data": $imgs.serialize().replace(/%5B%5D/g, '[]')}, function (data) { 333 | rti_callback(); 334 | }); 335 | }; 336 | 337 | var processForSave = function () { 338 | var $imgs = $('input.bpfb_photos_to_add'); 339 | var imgArr = []; 340 | $imgs.each(function () { 341 | imgArr[imgArr.length] = $(this).val(); 342 | }); 343 | return { 344 | "bpfb_photos": imgArr//$imgs.serialize().replace(/%5B%5D/g, '[]') 345 | }; 346 | }; 347 | 348 | var init = function () { 349 | $container.empty(); 350 | $('.bpfb_preview_container').empty(); 351 | $('.bpfb_action_container').empty(); 352 | $('#aw-whats-new-submit').hide(); 353 | createMarkup(); 354 | }; 355 | 356 | var destroy = function () { 357 | removeTempImages(function() { 358 | $container.empty(); 359 | $('.bpfb_preview_container').empty(); 360 | $('.bpfb_action_container').empty(); 361 | $('#aw-whats-new-submit').show(); 362 | }); 363 | }; 364 | 365 | removeTempImages(init); 366 | 367 | return {"destroy": destroy, "get": processForSave}; 368 | }; 369 | 370 | 371 | /* === End handlers === */ 372 | 373 | 374 | /** 375 | * Main interface markup creation. 376 | */ 377 | function createMarkup () { 378 | var html = '
    ' + 379 | '
    ' + 380 | '' + l10nBpfb.add_photos + '' + 381 | ' ' + 382 | '' + l10nBpfb.add_videos + '' + 383 | ' ' + 384 | '' + l10nBpfb.add_links + '' + 385 | '
    ' + 386 | '
    ' + 387 | '
    ' + 388 | '
    ' + 389 | '
    ' + 390 | '
    ' + 391 | '
    ' + 392 | '' + 393 | '
    '; 394 | $form.wrap('
    '); 395 | $textContainer.after(html); 396 | } 397 | 398 | 399 | /** 400 | * Initializes the main interface. 401 | */ 402 | function init () { 403 | $form = $("#whats-new-form"); 404 | $text = $form.find('textarea[name="whats-new"]'); 405 | $textContainer = $form.find('#whats-new-textarea'); 406 | createMarkup(); 407 | $('#bpfb_addPhotos').click(function () { 408 | if (_bpfbActiveHandler) _bpfbActiveHandler.destroy(); 409 | _bpfbActiveHandler = new BpfbPhotoHandler(); 410 | $("#bpfb_cancel_action").show(); 411 | return false; 412 | }); 413 | $('#bpfb_addLinks').click(function () { 414 | if (_bpfbActiveHandler) _bpfbActiveHandler.destroy(); 415 | _bpfbActiveHandler = new BpfbLinkHandler(); 416 | $("#bpfb_cancel_action").show(); 417 | return false; 418 | }); 419 | $('#bpfb_addVideos').click(function () { 420 | if (_bpfbActiveHandler) _bpfbActiveHandler.destroy(); 421 | _bpfbActiveHandler = new BpfbVideoHandler(); 422 | $("#bpfb_cancel_action").show(); 423 | return false; 424 | }); 425 | $('#bpfb_cancel_action').click(function () { 426 | $(".bpfb_toolbarItem.bpfb_active").removeClass("bpfb_active"); 427 | _bpfbActiveHandler.destroy(); 428 | $("#bpfb_cancel_action").hide(); 429 | return false; 430 | }); 431 | $(".bpfb_toolbarItem").click(function () { 432 | $(".bpfb_toolbarItem.bpfb_active").removeClass("bpfb_active"); 433 | $(this).addClass("bpfb_active"); 434 | }); 435 | $(document).on('click', '#bpfb_submit', function () { 436 | var params = _bpfbActiveHandler.get(); 437 | var group_id = $('#whats-new-post-in').length ? $('#whats-new-post-in').val() : 0; 438 | $.post(ajaxurl, { 439 | "action": "bpfb_update_activity_contents", 440 | "data": params, 441 | "content": $text.val(), 442 | "group_id": group_id 443 | }, function (data) { 444 | _bpfbActiveHandler.destroy(); 445 | $text.val(''); 446 | $('#activity-stream').prepend(data.activity); 447 | /** 448 | * Handle image scaling in previews. 449 | */ 450 | $(".bpfb_final_link img").each(function () { 451 | $(this).width($(this).parents('div').width()); 452 | }); 453 | }); 454 | }); 455 | $(document).on('click', '#bpfb_cancel', function () { 456 | $(".bpfb_toolbarItem.bpfb_active").removeClass("bpfb_active"); 457 | _bpfbActiveHandler.destroy(); 458 | }); 459 | } 460 | 461 | // Only initialize if we're supposed to. 462 | /* 463 | if ( 464 | !('ontouchstart' in document.documentElement) 465 | || 466 | ('ontouchstart' in document.documentElement && (/iPhone|iPod|iPad/i).test(navigator.userAgent)) 467 | ) { 468 | if ($("#whats-new-form").is(":visible")) init(); 469 | } 470 | */ 471 | // Meh, just do it - newish Droids seem to work fine. 472 | if ($("#whats-new-form").is(":visible")) init(); 473 | 474 | /** 475 | * Handle image scaling in previews. 476 | */ 477 | $(".bpfb_final_link img").each(function () { 478 | $(this).width($(this).parents('div').width()); 479 | }); 480 | 481 | }); 482 | })(jQuery); 483 | -------------------------------------------------------------------------------- /lib/class_bpfb_binder.php: -------------------------------------------------------------------------------- 1 | add_hooks(); 17 | } 18 | 19 | /** 20 | * Image moving and resizing routine. 21 | * 22 | * Relies on WP built-in image resizing. 23 | * 24 | * @param array Image paths to move from temp directory 25 | * @return mixed Array of new image paths, or (bool)false on failure. 26 | * @access private 27 | */ 28 | function move_images ($imgs) { 29 | if (!$imgs) return false; 30 | if (!is_array($imgs)) $imgs = array($imgs); 31 | 32 | global $bp; 33 | $ret = array(); 34 | 35 | list($thumb_w,$thumb_h) = Bpfb_Data::get_thumbnail_size(); 36 | 37 | $processed = 0; 38 | foreach ($imgs as $img) { 39 | $processed++; 40 | if (BPFB_IMAGE_LIMIT && $processed > BPFB_IMAGE_LIMIT) break; // Do not even bother to process more. 41 | if (preg_match('!^https?:\/\/!i', $img)) { // Just add remote images 42 | $ret[] = esc_url($img); 43 | continue; 44 | } 45 | 46 | $pfx = $bp->loggedin_user->id . '_' . preg_replace('/[^0-9]/', '-', microtime()); 47 | $tmp_img = realpath(BPFB_TEMP_IMAGE_DIR . $img); 48 | $new_img = BPFB_BASE_IMAGE_DIR . "{$pfx}_{$img}"; 49 | if (@rename($tmp_img, $new_img)) { 50 | if (function_exists('wp_get_image_editor')) { // New way of resizing the image 51 | $image = wp_get_image_editor($new_img); 52 | if (!is_wp_error($image)) { 53 | $thumb_filename = $image->generate_filename('bpfbt'); 54 | $image->resize($thumb_w, $thumb_h, false); 55 | 56 | // Alright, now let's rotate if we can 57 | if (function_exists('exif_read_data')) { 58 | $exif = exif_read_data($new_img); // Okay, we now have the data 59 | if (!empty($exif['Orientation']) && 3 === (int)$exif['Orientation']) $image->rotate(180); 60 | else if (!empty($exif['Orientation']) && 6 === (int)$exif['Orientation']) $image->rotate(-90); 61 | else if (!empty($exif['Orientation']) && 8 === (int)$exif['Orientation']) $image->rotate(90); 62 | } 63 | 64 | $image->save($thumb_filename); 65 | } 66 | } else { // Old school fallback 67 | image_resize($new_img, $thumb_w, $thumb_h, false, 'bpfbt'); 68 | } 69 | $ret[] = pathinfo($new_img, PATHINFO_BASENAME); 70 | } else return false; // Rename failure 71 | } 72 | 73 | return $ret; 74 | } 75 | 76 | /** 77 | * Sanitizes the path and expands it into full form. 78 | * 79 | * @param string $file Relative file path 80 | * 81 | * @return mixed Sanitized path, or (bool)false on failure 82 | */ 83 | public static function resolve_temp_path ($file) { 84 | $file = ltrim($file, '/'); 85 | 86 | // No subdirs in path, so we can do this quick check too 87 | if ($file !== basename($file)) return false; 88 | 89 | $tmp_path = trailingslashit(wp_normalize_path(realpath(BPFB_TEMP_IMAGE_DIR))); 90 | if (empty($tmp_path)) return false; 91 | 92 | $full_path = wp_normalize_path(realpath($tmp_path . $file)); 93 | if (empty($full_path)) return false; 94 | 95 | // Are we still within our defined TMP dir? 96 | $rx = preg_quote($tmp_path, '/'); 97 | $full_path = preg_match("/^{$rx}/", $full_path) 98 | ? $full_path 99 | : false 100 | ; 101 | if (empty($full_path)) return false; 102 | 103 | // Also, does this resolve to an actual file? 104 | return file_exists($full_path) 105 | ? $full_path 106 | : false 107 | ; 108 | } 109 | 110 | /** 111 | * Remote page retrieving routine. 112 | * 113 | * @param string Remote URL 114 | * @return mixed Remote page as string, or (bool)false on failure 115 | * @access private 116 | */ 117 | function get_page_contents ($url) { 118 | $response = wp_remote_get($url, array( 119 | 'user-agent' => 'BuddyPress Activity Plus', // Some sites will block default WP UA 120 | )); 121 | if (is_wp_error($response)) return false; 122 | 123 | $status = wp_remote_retrieve_response_code($response); 124 | if (200 !== (int)$status) return false; 125 | 126 | return $response['body']; 127 | } 128 | 129 | /** 130 | * Introduces `plugins_url()` and other significant URLs as root variables (global). 131 | */ 132 | function js_plugin_url () { 133 | $data = apply_filters( 134 | 'bpfb_js_data_object', 135 | array( 136 | 'root_url' => BPFB_PLUGIN_URL, 137 | 'temp_img_url' => BPFB_TEMP_IMAGE_URL, 138 | 'base_img_url' => BPFB_BASE_IMAGE_URL, 139 | 'theme' => Bpfb_Data::get('theme', 'default'), 140 | 'alignment' => Bpfb_Data::get('alignment', 'left'), 141 | ) 142 | ); 143 | printf('', json_encode($data)); 144 | if ('default' != $data['theme'] && !current_theme_supports('bpfb_toolbar_icons')) { 145 | $url = BPFB_PLUGIN_URL; 146 | echo << 148 | @font-face { 149 | font-family: 'bpfb'; 150 | src:url('{$url}/css/external/font/bpfb.eot'); 151 | src:url('{$url}/css/external/font/bpfb.eot?#iefix') format('embedded-opentype'), 152 | url('{$url}/css/external/font/bpfb.woff') format('woff'), 153 | url('{$url}/css/external/font/bpfb.ttf') format('truetype'), 154 | url('{$url}/css/external/font/bpfb.svg#icomoon') format('svg'); 155 | font-weight: normal; 156 | font-style: normal; 157 | } 158 | 159 | EOFontIconCSS; 160 | } 161 | } 162 | 163 | /** 164 | * Loads needed scripts and l10n strings for JS. 165 | */ 166 | function js_load_scripts () { 167 | wp_enqueue_script('jquery'); 168 | wp_enqueue_script('thickbox'); 169 | if (!current_theme_supports('bpfb_file_uploader')) { 170 | wp_enqueue_script('file_uploader', BPFB_PLUGIN_URL . '/js/external/fileuploader.js', array('jquery')); 171 | } 172 | wp_enqueue_script('bpfb_interface_script', BPFB_PLUGIN_URL . '/js/bpfb_interface.js', array('jquery')); 173 | wp_localize_script('bpfb_interface_script', 'l10nBpfb', array( 174 | 'add_photos_tip' => __('Add images', 'bpfb'), 175 | 'add_photos' => __('Submit images post', 'bpfb'), 176 | 'add_remote_image' => __('Add image URL', 'bpfb'), 177 | 'add_another_remote_image' => __('Add another image URL', 'bpfb'), 178 | 'add_videos' => __('Add videos', 'bpfb'), 179 | 'add_video' => __('Submit video post', 'bpfb'), 180 | 'add_links' => __('Add links', 'bpfb'), 181 | 'add_link' => __('Submit link post', 'bpfb'), 182 | 'add' => __('Add', 'bpfb'), 183 | 'cancel' => __('Cancel', 'bpfb'), 184 | 'preview' => __('Preview', 'bpfb'), 185 | 'drop_files' => __('Drop files here to upload', 'bpfb'), 186 | 'upload_file' => __('Upload a file', 'bpfb'), 187 | 'choose_thumbnail' => __('Choose thumbnail', 'bpfb'), 188 | 'no_thumbnail' => __('No thumbnail', 'bpfb'), 189 | 'paste_video_url' => __('Paste video URL here', 'bpfb'), 190 | 'paste_link_url' => __('Paste link here', 'bpfb'), 191 | 'images_limit_exceeded' => sprintf(__("You tried to add too many images, only %d will be posted.", 'bpfb'), BPFB_IMAGE_LIMIT), 192 | // Variables 193 | '_max_images' => BPFB_IMAGE_LIMIT, 194 | )); 195 | } 196 | 197 | /** 198 | * Loads required styles. 199 | */ 200 | function css_load_styles () { 201 | wp_enqueue_style('thickbox'); 202 | wp_enqueue_style('file_uploader_style', BPFB_PLUGIN_URL . '/css/external/fileuploader.css'); 203 | if (!current_theme_supports('bpfb_interface_style')) { 204 | wp_enqueue_style('bpfb_interface_style', BPFB_PLUGIN_URL . '/css/bpfb_interface.css'); 205 | } 206 | if (!current_theme_supports('bpfb_toolbar_icons')) { 207 | wp_enqueue_style('bpfb_toolbar_icons', BPFB_PLUGIN_URL . '/css/bpfb_toolbar.css'); 208 | } 209 | } 210 | 211 | /** 212 | * Handles video preview requests. 213 | */ 214 | function ajax_preview_video () { 215 | $url = !empty($_POST['data']) ? esc_url($_POST['data']) : false; 216 | $url = preg_match('/^https?:\/\//i', $url) ? $url : BPFB_PROTOCOL . $url; 217 | $warning = __('There has been an error processing your request', 'bpfb'); 218 | $response = $url ? __('Processing...', 'bpfb') : $warning; 219 | $ret = wp_oembed_get($url); 220 | echo ($ret ? $ret : $warning); 221 | exit(); 222 | } 223 | 224 | /** 225 | * Handles link preview requests. 226 | */ 227 | function ajax_preview_link () { 228 | $url = !empty($_POST['data']) ? esc_url($_POST['data']) : false; 229 | $scheme = parse_url($url, PHP_URL_SCHEME); 230 | if (!$scheme || !preg_match('/^https?$/', $scheme)) { 231 | $url = "http://{$url}"; 232 | } 233 | 234 | $warning = __('There has been an error processing your request', 'bpfb'); 235 | $response = $url ? __('Processing...', 'bpfb') : $warning; 236 | $images = array(); 237 | $title = $warning; 238 | $text = $warning; 239 | 240 | $page = $this->get_page_contents($url); 241 | if (!function_exists('str_get_html')) require_once(BPFB_PLUGIN_BASE_DIR . '/lib/external/simple_html_dom.php'); 242 | $html = str_get_html($page); 243 | $str = $html->find('text'); 244 | 245 | if ($str) { 246 | $image_els = $html->find('img'); 247 | foreach ($image_els as $el) { 248 | if ($el->width > 100 && $el->height > 1) // Disregard spacers 249 | $images[] = esc_url($el->src); 250 | } 251 | $og_image = $html->find('meta[property=og:image]', 0); 252 | if ($og_image) array_unshift($images, esc_url($og_image->content)); 253 | 254 | $title = $html->find('title', 0); 255 | $title = $title ? $title->plaintext: $url; 256 | 257 | $meta_description = $html->find('meta[name=description]', 0); 258 | $og_description = $html->find('meta[property=og:description]', 0); 259 | $first_paragraph = $html->find('p', 0); 260 | if ($og_description && $og_description->content) $text = $og_description->content; 261 | else if ($meta_description && $meta_description->content) $text = $meta_description->content; 262 | else if ($first_paragraph && $first_paragraph->plaintext) $text = $first_paragraph->plaintext; 263 | else $text = $title; 264 | 265 | $images = array_filter($images); 266 | } else { 267 | $url = ''; 268 | } 269 | 270 | header('Content-type: application/json'); 271 | echo json_encode(array( 272 | "url" => $url, 273 | "images" => $images, 274 | "title" => esc_attr($title), 275 | "text" => esc_attr($text), 276 | )); 277 | exit(); 278 | } 279 | 280 | /** 281 | * Handles image preview requests. 282 | * Relies on ./lib/external/file_uploader.php for images upload handling. 283 | * Stores images in the temporary storage. 284 | */ 285 | function ajax_preview_photo () { 286 | $dir = BPFB_PLUGIN_BASE_DIR . '/img/'; 287 | if (!class_exists('qqFileUploader')) require_once(BPFB_PLUGIN_BASE_DIR . '/lib/external/file_uploader.php'); 288 | $uploader = new qqFileUploader(self::_get_supported_image_extensions()); 289 | $result = $uploader->handleUpload(BPFB_TEMP_IMAGE_DIR); 290 | //header('Content-type: application/json'); // For some reason, IE doesn't like this. Skip. 291 | echo htmlspecialchars(json_encode($result), ENT_NOQUOTES); 292 | exit(); 293 | } 294 | 295 | /** 296 | * Handles remote images preview 297 | */ 298 | function ajax_preview_remote_image () { 299 | header('Content-type: application/json'); 300 | $data = !empty($_POST['data']) ? 301 | (is_array($_POST['data']) ? array_map('esc_url', $_POST['data']) : esc_url($_POST['data'])) 302 | : false 303 | ; 304 | echo json_encode($data); 305 | exit(); 306 | } 307 | 308 | /** 309 | * Clears up the temporary images storage. 310 | */ 311 | function ajax_remove_temp_images () { 312 | header('Content-type: application/json'); 313 | parse_str($_POST['data'], $data); 314 | $data = is_array($data) ? $data : array('bpfb_photos'=>array()); 315 | foreach ($data['bpfb_photos'] as $file) { 316 | $path = self::resolve_temp_path($file); 317 | if (!empty($path)) @unlink($path); 318 | } 319 | echo json_encode(array('status'=>'ok')); 320 | exit(); 321 | } 322 | 323 | /** 324 | * This is where we actually save the activity update. 325 | */ 326 | function ajax_update_activity_contents () { 327 | $bpfb_code = $activity = ''; 328 | $aid = 0; 329 | $codec = new BpfbCodec; 330 | 331 | if (!empty($_POST['data'])) { 332 | if (!empty($_POST['data']['bpfb_video_url'])) { 333 | $bpfb_code = $codec->create_video_tag($_POST['data']['bpfb_video_url']); 334 | } 335 | if (!empty($_POST['data']['bpfb_link_url'])) { 336 | $bpfb_code = $codec->create_link_tag( 337 | $_POST['data']['bpfb_link_url'], 338 | $_POST['data']['bpfb_link_title'], 339 | $_POST['data']['bpfb_link_body'], 340 | $_POST['data']['bpfb_link_image'] 341 | ); 342 | 343 | add_filter( 'bp_bypass_check_for_moderation', array( $this, 'bp_activity_link_moderation_custom'), 10, 4 ); 344 | } 345 | if (!empty($_POST['data']['bpfb_photos'])) { 346 | $images = $this->move_images($_POST['data']['bpfb_photos']); 347 | $bpfb_code = $codec->create_images_tag($images); 348 | } 349 | } 350 | 351 | $bpfb_code = apply_filters('bpfb_code_before_save', $bpfb_code); 352 | 353 | // All done creating tags. Now, save the code 354 | $gid = !empty($_POST['group_id']) && is_numeric($_POST['group_id']) 355 | ? (int)$_POST['group_id'] 356 | : false 357 | ; 358 | if ($bpfb_code) { 359 | $content = !empty($_POST['content']) ? $_POST['content'] : ''; 360 | $content .= "\n{$bpfb_code}"; 361 | $content = apply_filters('bp_activity_post_update_content', $content); 362 | $aid = $gid ? 363 | groups_post_update(array('content' => $content, 'group_id' => $gid)) 364 | : 365 | bp_activity_post_update(array('content' => $content)) 366 | ; 367 | global $blog_id; 368 | bp_activity_update_meta($aid, 'bpfb_blog_id', $blog_id); 369 | } 370 | if ($aid) { 371 | ob_start(); 372 | if ( bp_has_activities ( 'include=' . $aid ) ) { 373 | while ( bp_activities() ) { 374 | bp_the_activity(); 375 | if (function_exists('bp_locate_template')) bp_locate_template( array( 'activity/entry.php' ), true ); 376 | else locate_template( array( 'activity/entry.php' ), true ); 377 | } 378 | } 379 | $activity = ob_get_clean(); 380 | } 381 | header('Content-type: application/json'); 382 | echo json_encode(array( 383 | 'code' => $bpfb_code, 384 | 'id' => $aid, 385 | 'activity' => $activity, 386 | )); 387 | exit(); 388 | } 389 | 390 | function _add_js_css_hooks () { 391 | if (!is_user_logged_in()) return false; 392 | 393 | global $bp; 394 | 395 | $show_condition = (bool)( 396 | // Load the scripts on Activity pages 397 | (defined('BP_ACTIVITY_SLUG') && bp_is_activity_component()) 398 | || 399 | // Load the scripts when Activity page is the Home page 400 | (defined('BP_ACTIVITY_SLUG') && 'page' == get_option('show_on_front') && is_front_page() && BP_ACTIVITY_SLUG == get_option('page_on_front')) 401 | || 402 | // Load the script on Group home page 403 | (defined('BP_GROUPS_SLUG') && bp_is_groups_component() && 'home' == $bp->current_action) 404 | || 405 | apply_filters('bpfb_injection_additional_condition', false) 406 | ); 407 | 408 | if (apply_filters('bpfb_inject_dependencies', $show_condition)) { 409 | // Step1: Load JS/CSS requirements 410 | add_action('wp_enqueue_scripts', array($this, 'js_load_scripts')); 411 | add_action('wp_print_scripts', array($this, 'js_plugin_url')); 412 | add_action('wp_print_styles', array($this, 'css_load_styles')); 413 | 414 | do_action('bpfb_add_cssjs_hooks'); 415 | } 416 | } 417 | 418 | /** 419 | * Trigger handler when BuddyPress activity is removed. 420 | * @param array $args BuddyPress activity arguments 421 | * @return bool Insignificant 422 | */ 423 | function remove_activity_images ($args) { 424 | if (!is_user_logged_in()) return false; 425 | if (empty($args['id'])) return false; 426 | 427 | // Compatibility with BP Reshare 428 | if ($args['type'] == 'reshare_update') return false; 429 | 430 | $activity = new BP_Activity_Activity($args['id']); 431 | if (!is_object($activity) || empty($activity->content)) return false; 432 | 433 | if (!bp_activity_user_can_delete($activity)) return false; 434 | if (!BpfbCodec::has_images($activity->content)) return false; 435 | 436 | $matches = array(); 437 | preg_match('/\[bpfb_images\](.*?)\[\/bpfb_images\]/s', $activity->content, $matches); 438 | if (empty($matches[1])) return false; 439 | 440 | $this->_clean_up_content_images($matches[1], $activity); 441 | 442 | return true; 443 | } 444 | 445 | /** 446 | * Callback for activity images removal 447 | * @param string $content Shortcode content parsed for images 448 | * @param BP_Activity_Activity Activity which contains the shortcode - used for privilege check 449 | * @return bool 450 | */ 451 | private function _clean_up_content_images ($content, $activity) { 452 | if (!Bpfb_Data::get('cleanup_images')) return false; 453 | if (!bp_activity_user_can_delete($activity)) return false; 454 | 455 | $images = BpfbCodec::extract_images($content); 456 | if (empty($images)) return false; 457 | 458 | foreach ($images as $image) { 459 | $info = pathinfo(trim($image)); 460 | 461 | // Make sure we have the info we need 462 | if (empty($info['filename']) || empty($info['extension'])) continue; 463 | 464 | // Make sure we're dealing with the image 465 | $ext = strtolower($info['extension']); 466 | if (!in_array($ext, self::_get_supported_image_extensions())) continue; 467 | 468 | // Construct the filenames 469 | $thumbnail = bpfb_get_image_dir($activity_blog_id) . $info['filename'] . '-bpfbt.' . $ext; 470 | $full = bpfb_get_image_dir($activity_blog_id) . trim($image); 471 | 472 | // Actually remove the images 473 | if (file_exists($thumbnail) && is_writable($thumbnail)) @unlink($thumbnail); 474 | if (file_exists($full) && is_writable($full)) @unlink($full); 475 | } 476 | return true; 477 | } 478 | 479 | /** 480 | * Lists supported image extensions 481 | * @return array Supported image extensions 482 | */ 483 | private static function _get_supported_image_extensions () { 484 | return array('jpg', 'jpeg', 'png', 'gif'); 485 | } 486 | 487 | /** 488 | * This is where the plugin registers itself. 489 | */ 490 | function add_hooks () { 491 | 492 | add_action('init', array($this, '_add_js_css_hooks')); 493 | 494 | // Step2: Add AJAX request handlers 495 | add_action('wp_ajax_bpfb_preview_video', array($this, 'ajax_preview_video')); 496 | add_action('wp_ajax_bpfb_preview_link', array($this, 'ajax_preview_link')); 497 | add_action('wp_ajax_bpfb_preview_photo', array($this, 'ajax_preview_photo')); 498 | add_action('wp_ajax_bpfb_preview_remote_image', array($this, 'ajax_preview_remote_image')); 499 | add_action('wp_ajax_bpfb_remove_temp_images', array($this, 'ajax_remove_temp_images')); 500 | add_action('wp_ajax_bpfb_update_activity_contents', array($this, 'ajax_update_activity_contents')); 501 | 502 | do_action('bpfb_add_ajax_hooks'); 503 | 504 | // Step 3: Register and process shortcodes 505 | BpfbCodec::register(); 506 | 507 | if (Bpfb_Data::get('cleanup_images')) { 508 | add_action('bp_before_activity_delete', array($this, 'remove_activity_images')); 509 | } 510 | } 511 | 512 | /** 513 | * Bypass default wp moderation to tweak link count in post content. 514 | */ 515 | function bp_activity_link_moderation_custom( $bypass, $user_id, $title, $content ){ 516 | // Define local variable(s) 517 | $_post = array(); 518 | $match_out = ''; 519 | 520 | /** User Data *************************************************************/ 521 | 522 | if ( ! empty( $user_id ) ) { 523 | 524 | // Get author data 525 | $user = get_userdata( $user_id ); 526 | 527 | // If data exists, map it 528 | if ( ! empty( $user ) ) { 529 | $_post['author'] = $user->display_name; 530 | $_post['email'] = $user->user_email; 531 | $_post['url'] = $user->user_url; 532 | } 533 | } 534 | 535 | // Current user IP and user agent 536 | $_post['user_ip'] = bp_core_current_user_ip(); 537 | $_post['user_ua'] = bp_core_current_user_ua(); 538 | 539 | // Post title and content 540 | $_post['title'] = $title; 541 | $_post['content'] = $content; 542 | 543 | /** Max Links *************************************************************/ 544 | 545 | $max_links = get_option( 'comment_max_links' ); 546 | if ( ! empty( $max_links ) ) { 547 | 548 | $temp_content = str_replace(array("image='http", "image=\"http"), "", $content ); 549 | // How many links? 550 | $num_links = preg_match_all( '/(http|ftp|https):\/\//i', $temp_content, $match_out ); 551 | 552 | // Allow for bumping the max to include the user's URL 553 | if ( ! empty( $_post['url'] ) ) { 554 | 555 | /** 556 | * Filters the maximum amount of links allowed to include the user's URL. 557 | * 558 | * @since 1.6.0 559 | * 560 | * @param string $num_links How many links found. 561 | * @param string $value User's url. 562 | */ 563 | $num_links = apply_filters( 'comment_max_links_url', $num_links, $_post['url'] ); 564 | } 565 | 566 | // Das ist zu viele links! 567 | if ( ( $num_links >= $max_links ) && $num_links != 1 ) { 568 | return false; 569 | } 570 | } 571 | 572 | /** Blacklist *************************************************************/ 573 | 574 | // Get the moderation keys 575 | $blacklist = trim( get_option( 'moderation_keys' ) ); 576 | 577 | // Bail if blacklist is empty 578 | if ( ! empty( $blacklist ) ) { 579 | 580 | // Get words separated by new lines 581 | $words = explode( "\n", $blacklist ); 582 | 583 | // Loop through words 584 | foreach ( (array) $words as $word ) { 585 | 586 | // Trim the whitespace from the word 587 | $word = trim( $word ); 588 | 589 | // Skip empty lines 590 | if ( empty( $word ) ) { 591 | continue; 592 | } 593 | 594 | // Do some escaping magic so that '#' chars in the 595 | // spam words don't break things: 596 | $word = preg_quote( $word, '#' ); 597 | $pattern = "#$word#i"; 598 | 599 | // Loop through post data 600 | foreach ( $_post as $post_data ) { 601 | 602 | // Check each user data for current word 603 | if ( preg_match( $pattern, $post_data ) ) { 604 | 605 | // Post does not pass 606 | return false; 607 | } 608 | } 609 | } 610 | } 611 | 612 | // Check passed successfully 613 | return true; 614 | } 615 | } 616 | -------------------------------------------------------------------------------- /lib/external/simple_html_dom.php: -------------------------------------------------------------------------------- 1 | 6 | Acknowledge: Jose Solorzano (https://sourceforge.net/projects/php-html/) 7 | Contributions by: 8 | Yousuke Kumakura (Attribute filters) 9 | Vadim Voituk (Negative indexes supports of "find" method) 10 | Antcs (Constructor with automatically load contents either text or file/url) 11 | Licensed under The MIT License 12 | Redistributions of files must retain the above copyright notice. 13 | *******************************************************************************/ 14 | 15 | define('HDOM_TYPE_ELEMENT', 1); 16 | define('HDOM_TYPE_COMMENT', 2); 17 | define('HDOM_TYPE_TEXT', 3); 18 | define('HDOM_TYPE_ENDTAG', 4); 19 | define('HDOM_TYPE_ROOT', 5); 20 | define('HDOM_TYPE_UNKNOWN', 6); 21 | define('HDOM_QUOTE_DOUBLE', 0); 22 | define('HDOM_QUOTE_SINGLE', 1); 23 | define('HDOM_QUOTE_NO', 3); 24 | define('HDOM_INFO_BEGIN', 0); 25 | define('HDOM_INFO_END', 1); 26 | define('HDOM_INFO_QUOTE', 2); 27 | define('HDOM_INFO_SPACE', 3); 28 | define('HDOM_INFO_TEXT', 4); 29 | define('HDOM_INFO_INNER', 5); 30 | define('HDOM_INFO_OUTER', 6); 31 | define('HDOM_INFO_ENDSPACE',7); 32 | 33 | // helper functions 34 | // ----------------------------------------------------------------------------- 35 | // get html dom form file 36 | function file_get_html() { 37 | $dom = new simple_html_dom; 38 | $args = func_get_args(); 39 | $dom->load(call_user_func_array('file_get_contents', $args), true); 40 | return $dom; 41 | } 42 | 43 | // get html dom form string 44 | function str_get_html($str, $lowercase=true) { 45 | $dom = new simple_html_dom; 46 | $dom->load($str, $lowercase); 47 | return $dom; 48 | } 49 | 50 | // dump html dom tree 51 | function dump_html_tree($node, $show_attr=true, $deep=0) { 52 | $lead = str_repeat(' ', $deep); 53 | echo $lead.$node->tag; 54 | if ($show_attr && count($node->attr)>0) { 55 | echo '('; 56 | foreach($node->attr as $k=>$v) 57 | echo "[$k]=>\"".$node->$k.'", '; 58 | echo ')'; 59 | } 60 | echo "\n"; 61 | 62 | foreach($node->nodes as $c) 63 | dump_html_tree($c, $show_attr, $deep+1); 64 | } 65 | 66 | // get dom form file (deprecated) 67 | function file_get_dom() { 68 | $dom = new simple_html_dom; 69 | $args = func_get_args(); 70 | $dom->load(call_user_func_array('file_get_contents', $args), true); 71 | return $dom; 72 | } 73 | 74 | // get dom form string (deprecated) 75 | function str_get_dom($str, $lowercase=true) { 76 | $dom = new simple_html_dom; 77 | $dom->load($str, $lowercase); 78 | return $dom; 79 | } 80 | 81 | // simple html dom node 82 | // ----------------------------------------------------------------------------- 83 | class simple_html_dom_node { 84 | public $nodetype = HDOM_TYPE_TEXT; 85 | public $tag = 'text'; 86 | public $attr = array(); 87 | public $children = array(); 88 | public $nodes = array(); 89 | public $parent = null; 90 | public $_ = array(); 91 | private $dom = null; 92 | 93 | function __construct($dom) { 94 | $this->dom = $dom; 95 | $dom->nodes[] = $this; 96 | } 97 | 98 | function __destruct() { 99 | $this->clear(); 100 | } 101 | 102 | function __toString() { 103 | return $this->outertext(); 104 | } 105 | 106 | // clean up memory due to php5 circular references memory leak... 107 | function clear() { 108 | $this->dom = null; 109 | $this->nodes = null; 110 | $this->parent = null; 111 | $this->children = null; 112 | } 113 | 114 | // dump node's tree 115 | function dump($show_attr=true) { 116 | dump_html_tree($this, $show_attr); 117 | } 118 | 119 | // returns the parent of node 120 | function parent() { 121 | return $this->parent; 122 | } 123 | 124 | // returns children of node 125 | function children($idx=-1) { 126 | if ($idx===-1) return $this->children; 127 | if (isset($this->children[$idx])) return $this->children[$idx]; 128 | return null; 129 | } 130 | 131 | // returns the first child of node 132 | function first_child() { 133 | if (count($this->children)>0) return $this->children[0]; 134 | return null; 135 | } 136 | 137 | // returns the last child of node 138 | function last_child() { 139 | if (($count=count($this->children))>0) return $this->children[$count-1]; 140 | return null; 141 | } 142 | 143 | // returns the next sibling of node 144 | function next_sibling() { 145 | if ($this->parent===null) return null; 146 | $idx = 0; 147 | $count = count($this->parent->children); 148 | while ($idx<$count && $this!==$this->parent->children[$idx]) 149 | ++$idx; 150 | if (++$idx>=$count) return null; 151 | return $this->parent->children[$idx]; 152 | } 153 | 154 | // returns the previous sibling of node 155 | function prev_sibling() { 156 | if ($this->parent===null) return null; 157 | $idx = 0; 158 | $count = count($this->parent->children); 159 | while ($idx<$count && $this!==$this->parent->children[$idx]) 160 | ++$idx; 161 | if (--$idx<0) return null; 162 | return $this->parent->children[$idx]; 163 | } 164 | 165 | // get dom node's inner html 166 | function innertext() { 167 | if (isset($this->_[HDOM_INFO_INNER])) return $this->_[HDOM_INFO_INNER]; 168 | if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]); 169 | 170 | $ret = ''; 171 | foreach($this->nodes as $n) 172 | $ret .= $n->outertext(); 173 | return $ret; 174 | } 175 | 176 | // get dom node's outer text (with tag) 177 | function outertext() { 178 | if ($this->tag==='root') return $this->innertext(); 179 | 180 | // trigger callback 181 | if ($this->dom->callback!==null) 182 | call_user_func_array($this->dom->callback, array($this)); 183 | 184 | if (isset($this->_[HDOM_INFO_OUTER])) return $this->_[HDOM_INFO_OUTER]; 185 | if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]); 186 | 187 | // render begin tag 188 | $ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup(); 189 | 190 | // render inner text 191 | if (isset($this->_[HDOM_INFO_INNER])) 192 | $ret .= $this->_[HDOM_INFO_INNER]; 193 | else { 194 | foreach($this->nodes as $n) 195 | $ret .= $n->outertext(); 196 | } 197 | 198 | // render end tag 199 | if(isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END]!=0) 200 | $ret .= 'tag.'>'; 201 | return $ret; 202 | } 203 | 204 | // get dom node's plain text 205 | function text() { 206 | if (isset($this->_[HDOM_INFO_INNER])) return $this->_[HDOM_INFO_INNER]; 207 | switch ($this->nodetype) { 208 | case HDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]); 209 | case HDOM_TYPE_COMMENT: return ''; 210 | case HDOM_TYPE_UNKNOWN: return ''; 211 | } 212 | if (strcasecmp($this->tag, 'script')===0) return ''; 213 | if (strcasecmp($this->tag, 'style')===0) return ''; 214 | 215 | $ret = ''; 216 | foreach($this->nodes as $n) 217 | $ret .= $n->text(); 218 | return $ret; 219 | } 220 | 221 | function xmltext() { 222 | $ret = $this->innertext(); 223 | $ret = str_ireplace('', '', $ret); 225 | return $ret; 226 | } 227 | 228 | // build node's text with tag 229 | function makeup() { 230 | // text, comment, unknown 231 | if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]); 232 | 233 | $ret = '<'.$this->tag; 234 | $i = -1; 235 | 236 | foreach($this->attr as $key=>$val) { 237 | ++$i; 238 | 239 | // skip removed attribute 240 | if ($val===null || $val===false) 241 | continue; 242 | 243 | $ret .= $this->_[HDOM_INFO_SPACE][$i][0]; 244 | //no value attr: nowrap, checked selected... 245 | if ($val===true) 246 | $ret .= $key; 247 | else { 248 | switch($this->_[HDOM_INFO_QUOTE][$i]) { 249 | case HDOM_QUOTE_DOUBLE: $quote = '"'; break; 250 | case HDOM_QUOTE_SINGLE: $quote = '\''; break; 251 | default: $quote = ''; 252 | } 253 | $ret .= $key.$this->_[HDOM_INFO_SPACE][$i][1].'='.$this->_[HDOM_INFO_SPACE][$i][2].$quote.$val.$quote; 254 | } 255 | } 256 | $ret = $this->dom->restore_noise($ret); 257 | return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>'; 258 | } 259 | 260 | // find elements by css selector 261 | function find($selector, $idx=null) { 262 | $selectors = $this->parse_selector($selector); 263 | if (($count=count($selectors))===0) return array(); 264 | $found_keys = array(); 265 | 266 | // find each selector 267 | for ($c=0; $c<$count; ++$c) { 268 | if (($levle=count($selectors[0]))===0) return array(); 269 | if (!isset($this->_[HDOM_INFO_BEGIN])) return array(); 270 | 271 | $head = array($this->_[HDOM_INFO_BEGIN]=>1); 272 | 273 | // handle descendant selectors, no recursive! 274 | for ($l=0; $l<$levle; ++$l) { 275 | $ret = array(); 276 | foreach($head as $k=>$v) { 277 | $n = ($k===-1) ? $this->dom->root : $this->dom->nodes[$k]; 278 | $n->seek($selectors[$c][$l], $ret); 279 | } 280 | $head = $ret; 281 | } 282 | 283 | foreach($head as $k=>$v) { 284 | if (!isset($found_keys[$k])) 285 | $found_keys[$k] = 1; 286 | } 287 | } 288 | 289 | // sort keys 290 | ksort($found_keys); 291 | 292 | $found = array(); 293 | foreach($found_keys as $k=>$v) 294 | $found[] = $this->dom->nodes[$k]; 295 | 296 | // return nth-element or array 297 | if (is_null($idx)) return $found; 298 | else if ($idx<0) $idx = count($found) + $idx; 299 | return (isset($found[$idx])) ? $found[$idx] : null; 300 | } 301 | 302 | // seek for given conditions 303 | protected function seek($selector, &$ret) { 304 | list($tag, $key, $val, $exp, $no_key) = $selector; 305 | 306 | // xpath index 307 | if ($tag && $key && is_numeric($key)) { 308 | $count = 0; 309 | foreach ($this->children as $c) { 310 | if ($tag==='*' || $tag===$c->tag) { 311 | if (++$count==$key) { 312 | $ret[$c->_[HDOM_INFO_BEGIN]] = 1; 313 | return; 314 | } 315 | } 316 | } 317 | return; 318 | } 319 | 320 | $end = (!empty($this->_[HDOM_INFO_END])) ? $this->_[HDOM_INFO_END] : 0; 321 | if ($end==0) { 322 | $parent = $this->parent; 323 | while (!isset($parent->_[HDOM_INFO_END]) && $parent!==null) { 324 | $end -= 1; 325 | $parent = $parent->parent; 326 | } 327 | $end += $parent->_[HDOM_INFO_END]; 328 | } 329 | 330 | for($i=$this->_[HDOM_INFO_BEGIN]+1; $i<$end; ++$i) { 331 | $node = $this->dom->nodes[$i]; 332 | $pass = true; 333 | 334 | if ($tag==='*' && !$key) { 335 | if (in_array($node, $this->children, true)) 336 | $ret[$i] = 1; 337 | continue; 338 | } 339 | 340 | // compare tag 341 | if ($tag && $tag!=$node->tag && $tag!=='*') {$pass=false;} 342 | // compare key 343 | if ($pass && $key) { 344 | if ($no_key) { 345 | if (isset($node->attr[$key])) $pass=false; 346 | } 347 | else if (!isset($node->attr[$key])) $pass=false; 348 | } 349 | // compare value 350 | if ($pass && $key && $val && $val!=='*') { 351 | $check = $this->match($exp, $val, $node->attr[$key]); 352 | // handle multiple class 353 | if (!$check && strcasecmp($key, 'class')===0) { 354 | foreach(explode(' ',$node->attr[$key]) as $k) { 355 | $check = $this->match($exp, $val, $k); 356 | if ($check) break; 357 | } 358 | } 359 | if (!$check) $pass = false; 360 | } 361 | if ($pass) $ret[$i] = 1; 362 | unset($node); 363 | } 364 | } 365 | 366 | protected function match($exp, $pattern, $value) { 367 | switch ($exp) { 368 | case '=': 369 | return ($value===$pattern); 370 | case '!=': 371 | return ($value!==$pattern); 372 | case '^=': 373 | return preg_match("/^".preg_quote($pattern,'/')."/", $value); 374 | case '$=': 375 | return preg_match("/".preg_quote($pattern,'/')."$/", $value); 376 | case '*=': 377 | if ($pattern[0]=='/') 378 | return preg_match($pattern, $value); 379 | return preg_match("/".$pattern."/i", $value); 380 | } 381 | return false; 382 | } 383 | 384 | protected function parse_selector($selector_string) { 385 | // pattern of CSS selectors, modified from mootools 386 | $pattern = "/([\w-:\*]*)(?:\#([\w-]+)|\.([\w-]+))?(?:\[@?(!?[\w-]+)(?:([!*^$]?=)[\"']?(.*?)[\"']?)?\])?([\/, ]+)/is"; 387 | preg_match_all($pattern, trim($selector_string).' ', $matches, PREG_SET_ORDER); 388 | $selectors = array(); 389 | $result = array(); 390 | //print_r($matches); 391 | 392 | foreach ($matches as $m) { 393 | $m[0] = trim($m[0]); 394 | if ($m[0]==='' || $m[0]==='/' || $m[0]==='//') continue; 395 | // for borwser grnreated xpath 396 | if ($m[1]==='tbody') continue; 397 | 398 | list($tag, $key, $val, $exp, $no_key) = array($m[1], null, null, '=', false); 399 | if(!empty($m[2])) {$key='id'; $val=$m[2];} 400 | if(!empty($m[3])) {$key='class'; $val=$m[3];} 401 | if(!empty($m[4])) {$key=$m[4];} 402 | if(!empty($m[5])) {$exp=$m[5];} 403 | if(!empty($m[6])) {$val=$m[6];} 404 | 405 | // convert to lowercase 406 | if ($this->dom->lowercase) {$tag=strtolower($tag); $key=strtolower($key);} 407 | //elements that do NOT have the specified attribute 408 | if (isset($key[0]) && $key[0]==='!') {$key=substr($key, 1); $no_key=true;} 409 | 410 | $result[] = array($tag, $key, $val, $exp, $no_key); 411 | if (trim($m[7])===',') { 412 | $selectors[] = $result; 413 | $result = array(); 414 | } 415 | } 416 | if (count($result)>0) 417 | $selectors[] = $result; 418 | return $selectors; 419 | } 420 | 421 | function __get($name) { 422 | if (isset($this->attr[$name])) return $this->attr[$name]; 423 | switch($name) { 424 | case 'outertext': return $this->outertext(); 425 | case 'innertext': return $this->innertext(); 426 | case 'plaintext': return $this->text(); 427 | case 'xmltext': return $this->xmltext(); 428 | default: return array_key_exists($name, $this->attr); 429 | } 430 | } 431 | 432 | function __set($name, $value) { 433 | switch($name) { 434 | case 'outertext': return $this->_[HDOM_INFO_OUTER] = $value; 435 | case 'innertext': 436 | if (isset($this->_[HDOM_INFO_TEXT])) return $this->_[HDOM_INFO_TEXT] = $value; 437 | return $this->_[HDOM_INFO_INNER] = $value; 438 | } 439 | if (!isset($this->attr[$name])) { 440 | $this->_[HDOM_INFO_SPACE][] = array(' ', '', ''); 441 | $this->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE; 442 | } 443 | $this->attr[$name] = $value; 444 | } 445 | 446 | function __isset($name) { 447 | switch($name) { 448 | case 'outertext': return true; 449 | case 'innertext': return true; 450 | case 'plaintext': return true; 451 | } 452 | //no value attr: nowrap, checked selected... 453 | return (array_key_exists($name, $this->attr)) ? true : isset($this->attr[$name]); 454 | } 455 | 456 | function __unset($name) { 457 | if (isset($this->attr[$name])) 458 | unset($this->attr[$name]); 459 | } 460 | 461 | // camel naming conventions 462 | function getAllAttributes() {return $this->attr;} 463 | function getAttribute($name) {return $this->__get($name);} 464 | function setAttribute($name, $value) {$this->__set($name, $value);} 465 | function hasAttribute($name) {return $this->__isset($name);} 466 | function removeAttribute($name) {$this->__set($name, null);} 467 | function getElementById($id) {return $this->find("#$id", 0);} 468 | function getElementsById($id, $idx=null) {return $this->find("#$id", $idx);} 469 | function getElementByTagName($name) {return $this->find($name, 0);} 470 | function getElementsByTagName($name, $idx=null) {return $this->find($name, $idx);} 471 | function parentNode() {return $this->parent();} 472 | function childNodes($idx=-1) {return $this->children($idx);} 473 | function firstChild() {return $this->first_child();} 474 | function lastChild() {return $this->last_child();} 475 | function nextSibling() {return $this->next_sibling();} 476 | function previousSibling() {return $this->prev_sibling();} 477 | } 478 | 479 | // simple html dom parser 480 | // ----------------------------------------------------------------------------- 481 | class simple_html_dom { 482 | public $root = null; 483 | public $nodes = array(); 484 | public $callback = null; 485 | public $lowercase = false; 486 | protected $pos; 487 | protected $doc; 488 | protected $char; 489 | protected $size; 490 | protected $cursor; 491 | protected $parent; 492 | protected $noise = array(); 493 | protected $token_blank = " \t\r\n"; 494 | protected $token_equal = ' =/>'; 495 | protected $token_slash = " />\r\n\t"; 496 | protected $token_attr = ' >'; 497 | // use isset instead of in_array, performance boost about 30%... 498 | protected $self_closing_tags = array('img'=>1, 'br'=>1, 'input'=>1, 'meta'=>1, 'link'=>1, 'hr'=>1, 'base'=>1, 'embed'=>1, 'spacer'=>1); 499 | protected $block_tags = array('root'=>1, 'body'=>1, 'form'=>1, 'div'=>1, 'span'=>1, 'table'=>1); 500 | protected $optional_closing_tags = array( 501 | 'tr'=>array('tr'=>1, 'td'=>1, 'th'=>1), 502 | 'th'=>array('th'=>1), 503 | 'td'=>array('td'=>1), 504 | 'li'=>array('li'=>1), 505 | 'dt'=>array('dt'=>1, 'dd'=>1), 506 | 'dd'=>array('dd'=>1, 'dt'=>1), 507 | 'dl'=>array('dd'=>1, 'dt'=>1), 508 | 'p'=>array('p'=>1), 509 | 'nobr'=>array('nobr'=>1), 510 | ); 511 | 512 | function __construct($str=null) { 513 | if ($str) { 514 | if (preg_match("/^http:\/\//i",$str) || is_file($str)) 515 | $this->load_file($str); 516 | else 517 | $this->load($str); 518 | } 519 | } 520 | 521 | function __destruct() { 522 | $this->clear(); 523 | } 524 | 525 | // load html from string 526 | function load($str, $lowercase=true) { 527 | // prepare 528 | $this->prepare($str, $lowercase); 529 | // strip out comments 530 | $this->remove_noise("''is"); 531 | // strip out cdata 532 | $this->remove_noise("''is", true); 533 | // strip out