I'd like to customize my Wordpress site by extending the TinyMCE editor with a custom button.
Now I can find many tutorials on how to implement such "plugins", but neither of them seem to mention how to install it into Wordpress. It must be trivial to do so, but I can't seem to do it...
Lets say I have the following two files compressed into a zip file, but when I attempt to upload it to Wordpress I get:
Unpacking the packageā¦
Installing the pluginā¦
The package could not be installed. No valid plugins were found.
Plugin install failed.
Return to Plugins page
This is functions.php
<?php
// Add these functions to your functions.php file
// add the shortcode handler for YouTube videos
function addYouTube($atts, $content = null) {
extract(shortcode_atts(array( "id" => '' ), $atts));
return '<p style="text-align:center"><img src="http://img.youtube.com/vi/'.$id.'/0.jpg" width="400" height="300" class="aligncenter" /><span>Watch the video</span></p>';
}
add_shortcode('youtube', 'addYouTube');
function add_youtube_button() {
// Don't bother doing this stuff if the current user lacks permissions
if ( ! current_user_can('edit_posts') && ! current_user_can('edit_pages') )
return;
// Add only in Rich Editor mode
if ( get_user_option('rich_editing') == 'true') {
add_filter("mce_external_plugins", "add_youtube_tinymce_plugin");
add_filter('mce_buttons', 'register_youtube_button');
}
}
function register_youtube_button($buttons) {
array_push($buttons, "|", "youryoutube");
return $buttons;
}
// Load the TinyMCE plugin : editor_plugin.js (wp2.5)
function add_youtube_tinymce_plugin($plugin_array) {
$plugin_array['youryoutube'] = get_bloginfo('template_url').'/editor_plugin.js';
return $plugin_array;
}
function my_refresh_mce($ver) {
$ver += 3;
return $ver;
}
// init process for button control
add_filter( 'tiny_mce_version', 'my_refresh_mce');
add_action('init', 'add_youtube_button');
?>
And editor_plugin.js
(function() {
tinymce.create('tinymce.plugins.YourYouTube', {
init : function(ed, url) {
ed.addButton('youryoutube', {
title : 'youryoutube.youtube',
image : url+'/youtube.png',
onclick : function() {
idPattern = /(?:(?:[^v]+)+v.)?([^&=]{11})(?=&|$)/;
var vidId = prompt("YouTube Video", "Enter the id or url for your video");
var m = idPattern.exec(vidId);
if (m != null && m != 'undefined')
ed.execCommand('mceInsertContent', false, '[youtube id="'+m[1]+'"]');
}
});
},
createControl : function(n, cm) {
return null;
},
getInfo : function() {
return {
longname : "YouTube Shortcode",
author : 'Brett Terpstra',
authorurl : 'http://brettterpstra.com/',
infourl : 'http://brettterpstra.com/',
version : "1.0"
};
}
});
tinymce.PluginManager.add('youryoutube', tinymce.plugins.YourYouTube);
})();
The solution was that I was missing a header for the PHP file.
<?php
/**
* Plugin Name: Name Of The Plugin
* Plugin URI: http://URI_Of_Page_Describing_Plugin_and_Updates
* Description: A brief description of the Plugin.
* Version: The Plugin's Version Number, e.g.: 1.0
* Author: Name Of The Plugin Author
* Author URI: http://URI_Of_The_Plugin_Author
* License: A "Slug" license name e.g. GPL2
*/
Related
Is there any plugin to limit the number of images uploaded to a woo-commerce product
I have gone through different plugins but didn't find any that meets my requirement.
You can set limit to post for max attachments.
add_filter('wp_handle_upload_prefilter', 'limit_wp_handle_upload_prefilter');
function yoursite_wp_handle_upload_prefilter($file) {
// This bit is for the flash uploader
if ($file['type']=='application/octet-stream' && isset($file['tmp_name'])) {
$file_size = getimagesize($file['tmp_name']);
if (isset($file_size['error']) && $file_size['error']!=0) {
$file['error'] = "Unexpected Error: {$file_size['error']}";
return $file;
} else {
$file['type'] = $file_size['mime'];
}
}
if ($post_id = (isset($_REQUEST['post_id']) ? $_REQUEST['post_id'] : false)) {
if (count(get_posts("post_type=attachment&post_parent={$post_id}"))>4)
$file['error'] = "Sorry, you cannot upload more than four (5) image.";
}
return $file;
}
In WordPress I want to adapt the custom field in user profile to be a read only field.
I have tried achieving this with the Advanced Access Manager plugin, but I was not able to hide the field.
As far as I know, there are only three filters hooks in the Edit User and Profile pages: user_contactmethods, admin_color_scheme_picker and show_password_fields. Everything else has to be done with jQuery. replace () your_lable with your lable
add_action('admin_init', 'user_profile_fields_disable');
function user_profile_fields_disable() {
global $pagenow;
// apply only to user profile or user edit pages
if ($pagenow!=='profile.php' && $pagenow!=='user-edit.php') {
return;
}
// do not change anything for the administrator
if (current_user_can('administrator')) {
return;
}
add_action( 'admin_footer', 'user_profile_fields_disable_js' );
}
/**
* Disables selected fields in WP Admin user profile (profile.php, user-edit.php)
*/
function user_profile_fields_disable_js() {
?>
<script>
jQuery(document).ready( function($) {
var fields_to_disable = ['your_lable', 'role'];
for(i=0; i<fields_to_disable.length; i++) {
if ( $('#'+ fields_to_disable[i]).length ) {
$('#'+ fields_to_disable[i]).attr("disabled", "disabled");
}
}
});
</script>
<?php
}
Hi I'm using Word press visual composer .. the problem is that with some pages(not all pages) , the front editor is not working , it just keep loading endlessly ,at first it wasn't working with all pages .. but when I added a blank page , Front editor worked with the blank page and some other pages , so what to do to make visual composer front editor works with these pages?
I'm using word press the 7 theme.
this is my suffering:
There may be many reasons for this but the root is js error.
1. at first disable page "preloader" and try.
2. If not working try disabling other plugins for wordpress which may have js conflict.
3. The best way: Have a look on console log for js error in browser's developer tools and fix them.
i had problem with visual. In my case helped when i changed on classic mode and return to backend editor.
This issue is mostly related to composer-view.js, I faced the same issue and it got resolved after following this link -
visual-composer-templateget-is-not-a-functi and Visual Composer is not working
You need to replace old code of composer-view.js as follow. (ps:- backup your old file before replacing this code.)
You can find this file at following location-
wp-content/plugins/js_composer/assets/js/backend
Old Code
html2element: function ( html ) {
var attributes = {},
$template;
if ( _.isString( html ) ) {
this.template = _.template( html );
$template = $( this.template( this.model.toJSON(), vc.templateOptions.default ).trim() );
} else {
this.template = html;
$template = html;
}
_.each( $template.get( 0 ).attributes, function ( attr ) {
attributes[ attr.name ] = attr.value;
} );
this.$el.attr( attributes ).html( $template.html() );
this.setContent();
this.renderContent();
}
Replace with new one as follows-
html2element:function (html) {
var attributes = {},
$template;
if (_.isString(html)) {
this.template = _.template(html);
} else {
try {
this.template = _.template(html());
} catch (err) {
this.template = html;
}
}
$template = $(this.template(this.model.toJSON()).trim());
_.each($template.get(0).attributes, function (attr) {
attributes[attr.name] = attr.value;
});
this.$el.attr(attributes).html($template.html());
this.setContent();
this.renderContent();
}
then also replace the Render Function
Old code
render: function () {
var $shortcode_template_el = $( '#vc_shortcode-template-' + this.model.get( 'shortcode' ) );
if ( $shortcode_template_el.is( 'script' ) ) {
this.html2element( _.template( $shortcode_template_el.html(),
this.model.toJSON(),
vc.templateOptions.default ) );
} else {
var params = this.model.get( 'params' );
$.ajax( {
type: 'POST',
url: window.ajaxurl,
data: {
action: 'wpb_get_element_backend_html',
data_element: this.model.get( 'shortcode' ),
data_width: _.isUndefined( params.width ) ? '1/1' : params.width,
_vcnonce: window.vcAdminNonce
},
dataType: 'html',
context: this
} ).done( function ( html ) {
this.html2element( html );
} );
}
this.model.view = this;
this.$controls_buttons = this.$el.find( '.vc_controls > :first' );
return this;
}
Replace with new code as follows-
render: function () {
var $shortcode_template_el = $( '#vc_shortcode-template-' + this.model.get( 'shortcode' ) );
if ( $shortcode_template_el.is( 'script' ) ) {
var newHtmlCode = _.template( $shortcode_template_el.html(),
this.model.toJSON(),
vc.templateOptions.default );
if(!_.isString(newHtmlCode)){
newHtmlCode = $shortcode_template_el.html();
}
this.html2element( newHtmlCode );
} else {
var params = this.model.get( 'params' );
$.ajax( {
type: 'POST',
url: window.ajaxurl,
data: {
action: 'wpb_get_element_backend_html',
data_element: this.model.get( 'shortcode' ),
data_width: _.isUndefined( params.width ) ? '1/1' : params.width,
_vcnonce: window.vcAdminNonce
},
dataType: 'html',
context: this
} ).done( function ( html ) {
this.html2element( html );
} );
}
this.model.view = this;
this.$controls_buttons = this.$el.find( '.vc_controls > :first' );
return this;
}
I want to improve the process of uploading pictures in a Real Estate Website. This website is running WordPress 3.8. The theme offers front end submission with a very simple interface. The user selects the images (one by one) and then clicks to add. Finally when the user submit the listing all the images are uploaded at once. This is the screenshot of how it looks: Original Option: Listing Images.
This is the JQuery Plugin I am currently using,
/*!
* jQuery imagesLoaded plugin v2.1.1
* http://github.com/desandro/imagesloaded
*
* MIT License. by Paul Irish et al.
*/
/*jshint curly: true, eqeqeq: true, noempty: true, strict: true, undef: true, browser: true */
/*global jQuery: false */
;(function($, undefined) {
'use strict';
// blank image data-uri bypasses webkit log warning (thx doug jones)
var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
$.fn.imagesLoaded = function( callback ) {
var $this = this,
deferred = $.isFunction($.Deferred) ? $.Deferred() : 0,
hasNotify = $.isFunction(deferred.notify),
$images = $this.find('img').add( $this.filter('img') ),
loaded = [],
proper = [],
broken = [];
// Register deferred callbacks
if ($.isPlainObject(callback)) {
$.each(callback, function (key, value) {
if (key === 'callback') {
callback = value;
} else if (deferred) {
deferred[key](value);
}
});
}
function doneLoading() {
var $proper = $(proper),
$broken = $(broken);
if ( deferred ) {
if ( broken.length ) {
deferred.reject( $images, $proper, $broken );
} else {
deferred.resolve( $images );
}
}
if ( $.isFunction( callback ) ) {
callback.call( $this, $images, $proper, $broken );
}
}
function imgLoadedHandler( event ) {
imgLoaded( event.target, event.type === 'error' );
}
function imgLoaded( img, isBroken ) {
// don't proceed if BLANK image, or image is already loaded
if ( img.src === BLANK || $.inArray( img, loaded ) !== -1 ) {
return;
}
// store element in loaded images array
loaded.push( img );
// keep track of broken and properly loaded images
if ( isBroken ) {
broken.push( img );
} else {
proper.push( img );
}
// cache image and its state for future calls
$.data( img, 'imagesLoaded', { isBroken: isBroken, src: img.src } );
// trigger deferred progress method if present
if ( hasNotify ) {
deferred.notifyWith( $(img), [ isBroken, $images, $(proper), $(broken) ] );
}
// call doneLoading and clean listeners if all images are loaded
if ( $images.length === loaded.length ) {
setTimeout( doneLoading );
$images.unbind( '.imagesLoaded', imgLoadedHandler );
}
}
// if no images, trigger immediately
if ( !$images.length ) {
doneLoading();
} else {
$images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoadedHandler )
.each( function( i, el ) {
var src = el.src;
// find out if this image has been already checked for status
// if it was, and src has not changed, call imgLoaded on it
var cached = $.data( el, 'imagesLoaded' );
if ( cached && cached.src === src ) {
imgLoaded( el, cached.isBroken );
return;
}
// if complete is true and browser supports natural sizes, try
// to check for image status manually
if ( el.complete && el.naturalWidth !== undefined ) {
imgLoaded( el, el.naturalWidth === 0 || el.naturalHeight === 0 );
return;
}
// cached images don't fire load sometimes, so we reset src, but only when
// dealing with IE, or image is complete (loaded) and failed manual check
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
if ( el.readyState || el.complete ) {
el.src = BLANK;
el.src = src;
}
});
}
return deferred ? deferred.promise( $this ) : $this;
};
})(jQuery);
My goal is to have a more flexible system, where all the images can be selected at the same time and it starts loading right away. This will speed up the process and improve user experience. Also to arrange them in any order by moving them around. This is an example I found on another website. See screenshot: New Option: Multiple Image Upload
What programing language is good for this development? Any recommendations of where I can find code snippets for this application? Thanks in advance for your help!!
rough draft.....you need jquery and wordpress media js..just watch the js variable names below if there are errors it will be with these...
php in functions file:
if(function_exists( 'wp_enqueue_media' )){
wp_enqueue_media();
}
javascript...add to the page header..wp_enqueue_scripts or to your template (do this first to make sure its working!) you'll need your element called upload_image_button or change accordinely
// Uploading files
var media_uploader;
jQuery('.upload_image_button').live('click', function( event ){
var button = jQuery( this );
// If the media uploader already exists, reopen it.
if ( media_uploader ) {
media_uploader.open();
return;
}
// Create the media uploader.
media_uploader = wp.media.frames.media_uploader = wp.media({
title: button.data( 'uploader-title' ),
// Tell the modal to show only images.
library: {
type: 'image',
query: false
},
button: {
text: button.data( 'uploader-button-text' ),
},
multiple: button.data( 'uploader-allow-multiple' )
});
// Create a callback when the uploader is called
media_uploader.on( 'select', function() {
var selection = media_uploader.state().get('selection'),
input_name = button.data( 'input-name' ),
bucket = $( '#' + input_name + '-thumbnails');
selection.map( function( attachment ) {
attachment = attachment.toJSON();
// console.log(attachment);
bucket.append(function() {
return '<img src="'+attachment.sizes.thumbnail.url+'" width="'+attachment.sizes.thumbnail.width+'" height="'+attachment.sizes.thumbnail.height+'" class="submission_thumb thumbnail" /><input name="'+input_name+'[]" type="hidden" value="'+attachment.id+'" />'
});
});
});
// Open the uploader
media_uploader.open();
});
template file:
<span class="upload_image_button alt_button" data-input-name="images" data-uploader- title="Upload Images" data-uploader-button-text="Add to Submission" data-uploader-allow-multiple="true">Upload</span>
php $_POST return
if ( !empty( $_POST['submission_images'] ) ) {
// do something with the files, set featured img, add to content or save post_meta
}
or..............i came across a plugin that does this a lot better........sorry its in OOP and designed on back end but you can modify for front end! The problem with multi file uploader from WP is it required users to hit "CTRL" + click with no guidance....massive problem on front-end forms...this one you can add more guidance to easily...sorry i havent a frontend sample yet, i have yet to create :)
"Multi File Upload"
e.g.
public function render_meta_box_content($post)
{
// Add an nonce field so we can check for it later.
wp_nonce_field('miu_inner_custom_box', 'miu_inner_custom_box_nonce');
// Use get_post_meta to retrieve an existing value from the database.
$value = get_post_meta($post->ID, '_ad_images', true);
$metabox_content = '<div id="miu_images"></div><input type="button" onClick="addRow()" value="Add Image" class="button" />';
echo $metabox_content;
$images = unserialize($value);
$script = "<script>
itemsCount= 0;";
if (!empty($images))
{
foreach ($images as $image)
{
$script.="addRow('{$image}');";
}
}
$script .="</script>";
echo $script;
}
save function
public function save_image($post_id)
{
/*
* We need to verify this came from the our screen and with proper authorization,
* because save_post can be triggered at other times.
*/
// Check if our nonce is set.
if (!isset($_POST['miu_inner_custom_box_nonce']))
return $post_id;
$nonce = $_POST['miu_inner_custom_box_nonce'];
// Verify that the nonce is valid.
if (!wp_verify_nonce($nonce, 'miu_inner_custom_box'))
return $post_id;
// If this is an autosave, our form has not been submitted,
// so we don't want to do anything.
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
return $post_id;
// Check the user's permissions.
if ('page' == $_POST['post_type'])
{
if (!current_user_can('edit_page', $post_id))
return $post_id;
} else
{
if (!current_user_can('edit_post', $post_id))
return $post_id;
}
/* OK, its safe for us to save the data now. */
// Validate user input.
$posted_images = $_POST['miu_images'];
$miu_images = array();
foreach ($posted_images as $image_url)
{
if(!empty ($image_url))
$miu_images[] = esc_url_raw($image_url);
}
// Update the miu_images meta field.
update_post_meta($post_id, '_ad_images', serialize($miu_images));
}
js file
jQuery(document).ready(function(){
jQuery('.miu-remove').live( "click", function(e) {
e.preventDefault();
var id = jQuery(this).attr("id")
var btn = id.split("-");
var img_id = btn[1];
jQuery("#row-"+img_id ).remove();
});
var formfield;
var img_id;
jQuery('.Image_button').live( "click", function(e) {
e.preventDefault();
var id = jQuery(this).attr("id")
var btn = id.split("-");
img_id = btn[1];
jQuery('html').addClass('Image');
formfield = jQuery('#img-'+img_id).attr('name');
tb_show('', 'media-upload.php?type=image&TB_iframe=true');
return false;
});
window.original_send_to_editor = window.send_to_editor;
window.send_to_editor = function(html){
if (formfield) {
fileurl = jQuery('img',html).attr('src');
jQuery('#img-'+img_id).val(fileurl);
tb_remove();
jQuery('html').removeClass('Image');
} else {
window.original_send_to_editor(html);
}
};
});
function addRow(image_url){
if(typeof(image_url)==='undefined') image_url = "";
itemsCount+=1;
var emptyRowTemplate = '<div id=row-'+itemsCount+'> <input style=\'width:70%\' id=img- '+itemsCount+' type=\'text\' name=\'miu_images['+itemsCount+']\' value=\''+image_url+'\' />'
+'<input type=\'button\' href=\'#\' class=\'Image_button button\' id=\'Image_button- '+itemsCount+'\' value=\'Upload\'>'
+'<input class="miu-remove button" type=\'button\' value=\'Remove\' id=\'remove-'+itemsCount+'\' /></div>';
jQuery('#miu_images').append(emptyRowTemplate);
}
I have a WordPress installation and some plugins installed and activated. But I have lost the original plugins .zip files. I would like to install some of the plugins into a new WordPress.
I do not want to migrate/move/backup-restore the whole current WP installation. I just want to re-create the original plugin .zip files for some of the plugins that are installed. I do have access to the whole file tree as well as the database. Is there any method for doing that?
Yes, first add a download link to the plugin action links:
/**
* Add action links to each plugin
* #author brasofilo
*/
add_filter( 'plugin_action_links', function ( $plugin_meta, $plugin_file, $plugin_data, $status )
{
$plugin_dir = dirname( $plugin_file );
if( !empty( $plugin_dir ) && '.' !== $plugin_dir )
$plugin_meta[] = sprintf(
"<a href='#' class='down-zip down-icon' data-file='%s' title='%s'></a>",
$plugin_dir,
'Download zip file for ' . $plugin_data['Name']
);
else
$plugin_meta[] = "Root plugin, cannot zip";
return $plugin_meta;
}, 10, 4 );
Then style and attach JS actions:
/**
* Style and actions for wp-admin/plugins.php
* #author brasofilo
*/
add_action( 'admin_footer-plugins.php', function() {
?>
<style>
.down-icon:before { /* http://melchoyce.github.io/dashicons/ */
content: "\f316";
display: inline-block;
-webkit-font-smoothing: antialiased;
font: normal 20px/1 'dashicons';
vertical-align: top;
}
</style>
<script>
root_wp = '<?php echo WP_PLUGIN_DIR; ?>' + '/';
/**
* Virtual $_POST
* creates form, appends and submits
*
* #author https://stackoverflow.com/a/9815335/1287812
*/
function b5f_submit_form_post( path, params, method )
{
$ = jQuery;
method = method || "post"; // Set method to post by default, if not specified.
var form = $(document.createElement( "form" ))
.attr( {"method": method, "action": path} );
$.each( params, function(key,value)
{
$.each( value instanceof Array? value : [value], function(i,val)
{
$(document.createElement("input"))
.attr({ "type": "hidden", "name": key, "value": val })
.appendTo( form );
});
});
form.appendTo( document.body ).submit();
}
jQuery(document).ready(function($)
{
/**
* Fire a plugin download
*/
$("a.down-zip").click(function()
{
event.preventDefault();
b5f_submit_form_post( '', {
action: 'zip_a_plugin',
plugin_to_zip: root_wp + $(this).data('file'),
plugin_name: $(this).data('file')
});
});
});
</script>
<?php
});
Capture the custom $_POSTed data and process the plugin directory to zip:
/**
* Dispatch $_POST['action']=>'zip_a_plugin' custom action
* #author brasofilo https://stackoverflow.com/a/23546276/1287812
*/
add_action('admin_action_zip_a_plugin', function()
{
if( empty( $_REQUEST['plugin_to_zip'] ) )
return;
zipFile( $_REQUEST['plugin_to_zip'], $_REQUEST['plugin_name'], false );
});
And finally, using a zipping function found here on the Stack
/**
* Makes zip from folder
* #author https://stackoverflow.com/a/17585672/1287812
*/
function zipFile($source, $destination, $flag = '')
{
if ( !extension_loaded('zip') ) {
return false;
}
$zip = new ZipArchive();
$tmp_file = tempnam(WP_CONTENT_DIR,'');
if (!$zip->open($tmp_file, ZIPARCHIVE::CREATE)) {
return false;
}
$source = str_replace('\\', '/', realpath($source));
if($flag)
{
$flag = basename($source) . '/';
//$zip->addEmptyDir(basename($source) . '/');
}
if (is_dir($source) === true)
{
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
foreach ($files as $file)
{
$file = str_replace('\\', '/', realpath($file));
if (is_dir($file) === true)
{
$src = str_replace($source . '/', '', $flag.$file . '/');
if( WP_PLUGIN_DIR.'/' !== $src ) # Workaround, as it was creating a strange empty folder like /www_dev/dev.plugins/wp-content/plugins/
$zip->addEmptyDir( $src );
}
else if (is_file($file) === true)
{
$src = str_replace($source . '/', '', $flag.$file);
$zip->addFromString( $src, file_get_contents($file));
}
}
}
else if (is_file($source) === true)
{
$zip->addFromString($flag.basename($source), file_get_contents($source));
}
$tt = $zip->close();
if(file_exists($tmp_file))
{
// push to download the zip
header('Content-type: application/zip');
header('Content-Disposition: attachment; filename="'.$destination.'"');
readfile($tmp_file);
// remove zip file is exists in temp path
exit();
}
else {
echo $tt;
die();
}
}
Funnily enough, it's actually trivial to create a zip file from an existing plugin.
Simply create a zip file containing the plugin folder. The unix commands to do that are:
$ cd wp-content/plugins
$ zip -r my-plugin.zip my-plugin
You can then download the resulting my-plugin.zip file which can then be used in WordPress plugin install via upload on new sites (ie: WP Admin -> Plugins -> Add New -> Upload).
Obviously the zip file won't contain any database tables/mods, but most plugins test for this on install and perform any needed database upgrades at install time. Unfortunately there's no way to know whether this will be a problem without testing or checking the plugin source code, or both.
The easiest, code-less way is with another plugin, such as WordPress Downloader.