├── tinymce ├── langs │ ├── en_dlg.js │ └── langs.php ├── protect.png ├── editor_plugin.js └── tiny_mce_popup.js ├── .gitmodules ├── changelog.txt ├── languages └── psc-default.pot ├── README.md └── protect-selected-content.php /tinymce/langs/en_dlg.js: -------------------------------------------------------------------------------- 1 | tinyMCE.addI18n("en.protect", { 2 | title: "Password Protect" 3 | }); -------------------------------------------------------------------------------- /tinymce/protect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpmudev/protect-selected-content/master/tinymce/protect.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dash-notice"] 2 | path = dash-notice 3 | url = git@bitbucket.org:incsub/wpmudev-dashboard-notification.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /tinymce/langs/langs.php: -------------------------------------------------------------------------------- 1 | \n" 13 | "Language-Team: LANGUAGE \n" 14 | 15 | #: protect-selected-content.php:80 16 | msgid "This content is password protected. To view it please enter your password below:" 17 | msgstr "" 18 | 19 | #: protect-selected-content.php:81 20 | msgid "Password:" 21 | msgstr "" 22 | 23 | #: protect-selected-content.php:81 24 | msgid "Submit" 25 | msgstr "" 26 | 27 | #: protect-selected-content.php:151 28 | msgid "Password Protect Content" 29 | msgstr "" 30 | 31 | #: protect-selected-content.php:157 32 | msgid "Please enter a password!" 33 | msgstr "" 34 | 35 | #: protect-selected-content.php:161 36 | msgid "Password" 37 | msgstr "" 38 | 39 | #: protect-selected-content.php:165 40 | msgid "Enter a password to be applied to the selected content." 41 | msgstr "" 42 | 43 | #: protect-selected-content.php:173 44 | msgid "Cancel" 45 | msgstr "" 46 | 47 | #: protect-selected-content.php:177 48 | msgid "Insert" 49 | msgstr "" 50 | #. Plugin Name of the plugin/theme 51 | msgid "Password Protect Selected Content" 52 | msgstr "" 53 | 54 | #. Plugin URI of the plugin/theme 55 | msgid "http://premium.wpmudev.org/project/password-protect-selected-content/" 56 | msgstr "" 57 | 58 | #. Description of the plugin/theme 59 | msgid "Allows you to password protect selected content within a post or page while the rest of content remains public." 60 | msgstr "" 61 | 62 | #. Author of the plugin/theme 63 | msgid "WPMU DEV" 64 | msgstr "" 65 | 66 | #. Author URI of the plugin/theme 67 | msgid "http://premium.wpmudev.org/" 68 | msgstr "" 69 | -------------------------------------------------------------------------------- /tinymce/editor_plugin.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // Load plugin specific language pack 3 | tinymce.PluginManager.requireLangPack('protect'); 4 | 5 | tinymce.create('tinymce.plugins.ProtectPlugin', { 6 | /** 7 | * Initializes the plugin, this will be executed after the plugin has been created. 8 | * This call is done before the editor instance has finished it's initialization so use the onInit event 9 | * of the editor instance to intercept that event. 10 | * 11 | * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. 12 | * @param {string} url Absolute URL to where the plugin is located. 13 | */ 14 | init : function(ed, url) { 15 | // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceChat'); 16 | ed.addCommand('mceProtect', function() { 17 | ed.windowManager.open({ 18 | file : ajaxurl + "?action=protectTinymceOptions", 19 | width : 480, 20 | height : 100, 21 | inline : 1 22 | }, { 23 | plugin_url : url // Plugin absolute URL 24 | }); 25 | }); 26 | 27 | // Register chat button 28 | ed.addButton('protect', { 29 | title : ed.getLang('protect.title'), 30 | cmd : 'mceProtect', 31 | image : url + '/protect.png' 32 | }); 33 | 34 | // Add a node change handler, selects the button in the UI when a image is selected 35 | ed.onNodeChange.add(function(ed, cm, n) { 36 | cm.setActive('protect', n.nodeName == 'IMG'); 37 | }); 38 | }, 39 | 40 | /** 41 | * Creates control instances based in the incomming name. This method is normally not 42 | * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons 43 | * but you sometimes need to create more complex controls like listboxes, split buttons etc then this 44 | * method can be used to create those. 45 | * 46 | * @param {String} n Name of the control to create. 47 | * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control. 48 | * @return {tinymce.ui.Control} New control instance or null if no control was created. 49 | */ 50 | createControl : function(n, cm) { 51 | return null; 52 | }, 53 | 54 | /** 55 | * Returns information about the plugin as a name/value array. 56 | * The current keys are longname, author, authorurl, infourl and version. 57 | * 58 | * @return {Object} Name/value array containing information about the plugin. 59 | */ 60 | getInfo : function() { 61 | return { 62 | longname : 'Protect Plugin', 63 | author : 'Aaron Edwards', 64 | authorurl : 'http://premium.wpmudev.org', 65 | infourl : 'http://premium.wpmudev.org/project/partial-post-password', 66 | version : "1.0" 67 | }; 68 | } 69 | }); 70 | 71 | // Register plugin 72 | tinymce.PluginManager.add('protect', tinymce.plugins.ProtectPlugin); 73 | })(); 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Password Protect 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 | ## Password Protect lets you easily hide content in any post or page behind a safe and secure password. 10 | 11 | No complex membership system required. 12 | 13 | ### Simple Password Protection 14 | 15 | Just select the content you'd like to hide (it can be a video, audio track, paragraph, answer or anything else at all), click on the lock button and select a password that must be entered. Once activated  you'll see a nice new 'Password Protect' icon added to your Visual editor that you can use to password protect selected content. 16 | 17 | ![password-protect-button](https://premium.wpmudev.org/wp-content/uploads/2011/03/password-protect-button.png) 18 | 19 | That little lock is all you need 20 | 21 | Simply select the content you want to protect and enter the password you'd like to use. You can even easily edit or change it at a later date. 22 | 23 | ![protecting-with-a-password](https://premium.wpmudev.org/wp-content/uploads/2011/03/protecting-with-a-password.png) 24 | 25 | Fun game... guess the password 26 | 27 | Protected content will prompt users to enter a password for access to your content. 28 | 29 | ![Time to enter your password](https://premium.wpmudev.org/wp-content/uploads/2011/03/password-protected-video.png) 30 | 31 | Time to enter your password 32 | 33 | And presto... 34 | 35 | ![After the password has been entered](https://premium.wpmudev.org/wp-content/uploads/2011/03/after-password.png) 36 | 37 | After the password has been entered 38 | 39 | ### Protect Just About Anything 40 | 41 | You can protect any media, shortcodes inserted by other plugins or any other digital item you'd like - if it can be inserted into a post or page, it can be protected. Plus, multiple separate pieces of content can be protected in a single post or page. An excellent tool for: 42 | 43 | * Teachers wanting to keep work private from everyone who isn't in their class (or student group) 44 | * Bloggers wanting to limit access to content to only people they know 45 | * Providing special giveaways, but only to people who you give, or who can guess the password 46 | * Or anyone looking to secure a piece of content, rather than a whole page 47 | 48 | Use Password Protect for a simple content protection solution. 49 | 50 | ## Usage 51 | 52 | Start by reading [Installing plugins](https://premium.wpmudev.org/project/ab-theme-testing/project/wordpress-wiki/wpmu-manual/installing-regular-plugins-on-wpmu/) section in our comprehensive [WordPress and WordPress Multisite Manual](https://premium.wpmudev.org/project/ab-theme-testing/project/wordpress-wiki/wpmu-manual/) if you are new to WordPress. 53 | 54 | ### To install: 55 | 56 | 1.  Download the plugin file 57 | 58 | 2.  Unzip the file into a folder on your hard drive 59 | 60 | 3.  Upload **/protect-selected-content/** folder to **/wp-content/plugins/** folder on your site 61 | 62 | 4.  Login to your admin panel for WordPress or Multisite and activate the plugin: 63 | 64 | * On regular WordPress installs - visit **Plugins** and **Activate** the plugin. 65 | * For WordPress Multisite installs - Activate it blog-by-blog (say if you wanted to make it a Supporter premium plugin), or visit **Network Admin -> Plugins** and **Network Activate** the plugin. 66 | 67 | ### To Use: 68 | 69 | Once activated you'll see a nice new 'Password Protect' icon added to your Visual editor that you can use to password protect selected content. 70 | 71 | ![Password Protect icon](https://premium.wpmudev.org/wp-content/uploads/2011/03/pass65.jpg) 72 | 73 | Now it is a simple as select your content to protect and add the password. 74 | 75 | ![Password protecting selection](https://premium.wpmudev.org/wp-content/uploads/2011/03/pass62.jpg) 76 | 77 | After you've entered the password it inserts a shortcode around your selected content. 78 | 79 | ![Example of the content wrapped inside password protect shortcode ](https://premium.wpmudev.org/wp-content/uploads/2011/03/pass64.jpg) 80 | 81 | On the front end, entering the password works the same as post passwords, it's stored in an encrypted cookie for 10 days. 82 | 83 | ![Example of a post with selected](https://premium.wpmudev.org/wp-content/uploads/2011/03/pass63.jpg) 84 | -------------------------------------------------------------------------------- /protect-selected-content.php: -------------------------------------------------------------------------------- 1 | false 64 | ), $atts ) ); 65 | 66 | //skip check for no content 67 | if ( is_null( $content ) ) 68 | return; 69 | 70 | //if no pass set don't protect 71 | if ( !$password ) 72 | return do_shortcode( $content ); 73 | 74 | //check cookie for password 75 | if ( isset( $_COOKIE['psc-postpass_' . COOKIEHASH] ) && $_COOKIE['psc-postpass_' . COOKIEHASH] == sha1( $password ) ) { 76 | return do_shortcode( $content ); 77 | } else { 78 | $label = 'pwbox-' . rand(); 79 | return '
80 |

' . __("This content is password protected. To view it please enter your password below:", 'psc') . '

81 |

82 |
83 | '; 84 | } 85 | } 86 | 87 | function set_password() { 88 | 89 | if ( get_magic_quotes_gpc() ) 90 | $_POST['post_password'] = stripslashes( $_POST['post_password'] ); 91 | 92 | //set cookie for 10 days 93 | setcookie( 'psc-postpass_' . COOKIEHASH, sha1( $_POST['post_password'] ), time() + 864000, COOKIEPATH ); 94 | 95 | //jump back to post 96 | wp_safe_redirect( wp_get_referer() ); 97 | exit; 98 | } 99 | 100 | function load_tinymce() { 101 | if ( (current_user_can('edit_posts') || current_user_can('edit_pages')) && get_user_option('rich_editing') == 'true') { 102 | add_filter( 'mce_external_plugins', array(&$this, 'tinymce_add_plugin') ); 103 | add_filter( 'mce_buttons', array(&$this,'tinymce_register_button') ); 104 | add_filter( 'mce_external_languages', array(&$this,'tinymce_load_langs') ); 105 | } 106 | } 107 | 108 | /** 109 | * TinyMCE dialog content 110 | */ 111 | function tinymce_options() { 112 | ?> 113 | 114 | 115 | 116 | 117 | 122 | 125 | 126 | 127 | 128 | 129 | 130 | 151 | 157 | 158 | <?php _e("Password Protect Content", 'psc'); ?> 159 | 160 | 161 |
162 | 163 |
164 | 165 |
166 | 167 | 168 | 169 | 172 | 173 | 174 |
170 | 171 |
175 |
176 |
177 | 178 |
179 |
180 | " onclick="tinyMCEPopup.close();" /> 181 |
182 | 183 |
184 | " /> 185 |
186 |
187 |
188 | 189 | 190 | /langs/_dlg.js lang pack file. 223 | * 224 | * @method requireLangPack 225 | */ 226 | requireLangPack: function () { 227 | var self = this, url = self.getWindowArg('plugin_url') || self.getWindowArg('theme_url'), settings = self.editor.settings, lang; 228 | 229 | if (settings.language !== false) { 230 | lang = settings.language || "en"; 231 | } 232 | 233 | if (url && lang && self.features.translate_i18n !== false && settings.language_load !== false) { 234 | url += '/langs/' + lang + '_dlg.js'; 235 | 236 | if (!tinymce.ScriptLoader.isDone(url)) { 237 | document.write(''); 238 | tinymce.ScriptLoader.markDone(url); 239 | } 240 | } 241 | }, 242 | 243 | /** 244 | * Executes a color picker on the specified element id. When the user 245 | * then selects a color it will be set as the value of the specified element. 246 | * 247 | * @method pickColor 248 | * @param {DOMEvent} e DOM event object. 249 | * @param {string} element_id Element id to be filled with the color value from the picker. 250 | */ 251 | pickColor: function (e, element_id) { 252 | var el = document.getElementById(element_id), colorPickerCallback = this.editor.settings.color_picker_callback; 253 | if (colorPickerCallback) { 254 | colorPickerCallback.call( 255 | this.editor, 256 | function (value) { 257 | el.value = value; 258 | try { 259 | el.onchange(); 260 | } catch (ex) { 261 | // Try fire event, ignore errors 262 | } 263 | }, 264 | el.value 265 | ); 266 | } 267 | }, 268 | 269 | /** 270 | * Opens a filebrowser/imagebrowser this will set the output value from 271 | * the browser as a value on the specified element. 272 | * 273 | * @method openBrowser 274 | * @param {string} element_id Id of the element to set value in. 275 | * @param {string} type Type of browser to open image/file/flash. 276 | * @param {string} option Option name to get the file_broswer_callback function name from. 277 | */ 278 | openBrowser: function (element_id, type) { 279 | tinyMCEPopup.restoreSelection(); 280 | this.editor.execCallback('file_browser_callback', element_id, document.getElementById(element_id).value, type, window); 281 | }, 282 | 283 | /** 284 | * Creates a confirm dialog. Please don't use the blocking behavior of this 285 | * native version use the callback method instead then it can be extended. 286 | * 287 | * @method confirm 288 | * @param {String} t Title for the new confirm dialog. 289 | * @param {function} cb Callback function to be executed after the user has selected ok or cancel. 290 | * @param {Object} s Optional scope to execute the callback in. 291 | */ 292 | confirm: function (t, cb, s) { 293 | this.editor.windowManager.confirm(t, cb, s, window); 294 | }, 295 | 296 | /** 297 | * Creates a alert dialog. Please don't use the blocking behavior of this 298 | * native version use the callback method instead then it can be extended. 299 | * 300 | * @method alert 301 | * @param {String} tx Title for the new alert dialog. 302 | * @param {function} cb Callback function to be executed after the user has selected ok. 303 | * @param {Object} s Optional scope to execute the callback in. 304 | */ 305 | alert: function (tx, cb, s) { 306 | this.editor.windowManager.alert(tx, cb, s, window); 307 | }, 308 | 309 | /** 310 | * Closes the current window. 311 | * 312 | * @method close 313 | */ 314 | close: function () { 315 | var t = this; 316 | 317 | // To avoid domain relaxing issue in Opera 318 | function close() { 319 | t.editor.windowManager.close(window); 320 | tinymce = tinyMCE = t.editor = t.params = t.dom = t.dom.doc = null; // Cleanup 321 | } 322 | 323 | if (tinymce.isOpera) { 324 | t.getWin().setTimeout(close, 0); 325 | } else { 326 | close(); 327 | } 328 | }, 329 | 330 | // Internal functions 331 | 332 | _restoreSelection: function () { 333 | var e = window.event.srcElement; 334 | 335 | if (e.nodeName == 'INPUT' && (e.type == 'submit' || e.type == 'button')) { 336 | tinyMCEPopup.restoreSelection(); 337 | } 338 | }, 339 | 340 | /* _restoreSelection : function() { 341 | var e = window.event.srcElement; 342 | 343 | // If user focus a non text input or textarea 344 | if ((e.nodeName != 'INPUT' && e.nodeName != 'TEXTAREA') || e.type != 'text') 345 | tinyMCEPopup.restoreSelection(); 346 | },*/ 347 | 348 | _onDOMLoaded: function () { 349 | var t = tinyMCEPopup, ti = document.title, h, nv; 350 | 351 | // Translate page 352 | if (t.features.translate_i18n !== false) { 353 | var map = { 354 | "update": "Ok", 355 | "insert": "Ok", 356 | "cancel": "Cancel", 357 | "not_set": "--", 358 | "class_name": "Class name", 359 | "browse": "Browse" 360 | }; 361 | 362 | var langCode = (tinymce.settings ? tinymce.settings : t.editor.settings).language || 'en'; 363 | for (var key in map) { 364 | tinymce.i18n.data[langCode + "." + key] = tinymce.i18n.translate(map[key]); 365 | } 366 | 367 | h = document.body.innerHTML; 368 | 369 | // Replace a=x with a="x" in IE 370 | if (tinymce.isIE) { 371 | h = h.replace(/ (value|title|alt)=([^"][^\s>]+)/gi, ' $1="$2"'); 372 | } 373 | 374 | document.dir = t.editor.getParam('directionality', ''); 375 | 376 | if ((nv = t.editor.translate(h)) && nv != h) { 377 | document.body.innerHTML = nv; 378 | } 379 | 380 | if ((nv = t.editor.translate(ti)) && nv != ti) { 381 | document.title = ti = nv; 382 | } 383 | } 384 | 385 | if (!t.editor.getParam('browser_preferred_colors', false) || !t.isWindow) { 386 | t.dom.addClass(document.body, 'forceColors'); 387 | } 388 | 389 | document.body.style.display = ''; 390 | 391 | // Restore selection in IE when focus is placed on a non textarea or input element of the type text 392 | if (tinymce.Env.ie) { 393 | if (tinymce.Env.ie < 11) { 394 | document.attachEvent('onmouseup', tinyMCEPopup._restoreSelection); 395 | 396 | // Add base target element for it since it would fail with modal dialogs 397 | t.dom.add(t.dom.select('head')[0], 'base', { target: '_self' }); 398 | } else { 399 | document.addEventListener('mouseup', tinyMCEPopup._restoreSelection, false); 400 | } 401 | } 402 | 403 | t.restoreSelection(); 404 | t.resizeToInnerSize(); 405 | 406 | // Set inline title 407 | if (!t.isWindow) { 408 | t.editor.windowManager.setTitle(window, ti); 409 | } else { 410 | window.focus(); 411 | } 412 | 413 | if (!tinymce.isIE && !t.isWindow) { 414 | t.dom.bind(document, 'focus', function () { 415 | t.editor.windowManager.focus(t.id); 416 | }); 417 | } 418 | 419 | // Patch for accessibility 420 | tinymce.each(t.dom.select('select'), function (e) { 421 | e.onkeydown = tinyMCEPopup._accessHandler; 422 | }); 423 | 424 | // Call onInit 425 | // Init must be called before focus so the selection won't get lost by the focus call 426 | tinymce.each(t.listeners, function (o) { 427 | o.func.call(o.scope, t.editor); 428 | }); 429 | 430 | // Move focus to window 431 | if (t.getWindowArg('mce_auto_focus', true)) { 432 | window.focus(); 433 | 434 | // Focus element with mceFocus class 435 | tinymce.each(document.forms, function (f) { 436 | tinymce.each(f.elements, function (e) { 437 | if (t.dom.hasClass(e, 'mceFocus') && !e.disabled) { 438 | e.focus(); 439 | return false; // Break loop 440 | } 441 | }); 442 | }); 443 | } 444 | 445 | document.onkeyup = tinyMCEPopup._closeWinKeyHandler; 446 | 447 | if ('textContent' in document) { 448 | t.uiWindow.getEl('head').firstChild.textContent = document.title; 449 | } else { 450 | t.uiWindow.getEl('head').firstChild.innerText = document.title; 451 | } 452 | }, 453 | 454 | _accessHandler: function (e) { 455 | e = e || window.event; 456 | 457 | if (e.keyCode == 13 || e.keyCode == 32) { 458 | var elm = e.target || e.srcElement; 459 | 460 | if (elm.onchange) { 461 | elm.onchange(); 462 | } 463 | 464 | return tinymce.dom.Event.cancel(e); 465 | } 466 | }, 467 | 468 | _closeWinKeyHandler: function (e) { 469 | e = e || window.event; 470 | 471 | if (e.keyCode == 27) { 472 | tinyMCEPopup.close(); 473 | } 474 | }, 475 | 476 | _eventProxy: function (id) { 477 | return function (evt) { 478 | tinyMCEPopup.dom.events.callNativeHandler(id, evt); 479 | }; 480 | } 481 | }; 482 | 483 | tinyMCEPopup.init(); 484 | 485 | tinymce.util.Dispatcher = function (scope) { 486 | this.scope = scope || this; 487 | this.listeners = []; 488 | 489 | this.add = function (callback, scope) { 490 | this.listeners.push({ cb: callback, scope: scope || this.scope }); 491 | 492 | return callback; 493 | }; 494 | 495 | this.addToTop = function (callback, scope) { 496 | var self = this, listener = { cb: callback, scope: scope || self.scope }; 497 | 498 | // Create new listeners if addToTop is executed in a dispatch loop 499 | if (self.inDispatch) { 500 | self.listeners = [listener].concat(self.listeners); 501 | } else { 502 | self.listeners.unshift(listener); 503 | } 504 | 505 | return callback; 506 | }; 507 | 508 | this.remove = function (callback) { 509 | var listeners = this.listeners, output = null; 510 | 511 | tinymce.each(listeners, function (listener, i) { 512 | if (callback == listener.cb) { 513 | output = listener; 514 | listeners.splice(i, 1); 515 | return false; 516 | } 517 | }); 518 | 519 | return output; 520 | }; 521 | 522 | this.dispatch = function () { 523 | var self = this, returnValue, args = arguments, i, listeners = self.listeners, listener; 524 | 525 | self.inDispatch = true; 526 | 527 | // Needs to be a real loop since the listener count might change while looping 528 | // And this is also more efficient 529 | for (i = 0; i < listeners.length; i++) { 530 | listener = listeners[i]; 531 | returnValue = listener.cb.apply(listener.scope, args.length > 0 ? args : [listener.scope]); 532 | 533 | if (returnValue === false) { 534 | break; 535 | } 536 | } 537 | 538 | self.inDispatch = false; 539 | 540 | return returnValue; 541 | }; 542 | }; 543 | --------------------------------------------------------------------------------