I'm trying to use the Media Uploader in a plugin.
I need to pass additional parameters to the query-attachment request.
I've tried to add a parameter to the Library like this:
file_frame = wp.media.frames.file_frame = wp.media({
title: "User's photo",
button: {
text: "Upload image",
},
library: {
type: ['image/jpeg','image/png'],
--> additional_param: 'a value'
},
multiple: false // Set to true to allow multiple files to be selected
});
But this additional parameter seems to screw up the new attachment upload process - the new uploaded attachment isn't display in the grid at the download end.
Is there a valid way to add a custom parameter to the query-attachment request?
Thank you for your answers
I dug a little bit un process and the best way to pass additional parameter I found is to add it during the query call.
To do it, several classes add to be extended.
Extending Query (wp.media.model.Query)
The sync Query's method is responsible to place the actual admin-ajax call to query the attachments. The sync method has 3 arguments, the last one - options - contains the parameters passed to the http request. The extended sync method adds the additional parameter before calling the parent sync request.
// Extending the wp.media.query to add a parameter
var Query1;
Query1=wp.media.model.Query1 = oldQuery.extend({
sync: function( method, model, options ) {
options = options || {};
options = _.extend(options, {'data':{'CadTest': '1'}});
return oldQuery.prototype.sync.apply(this, arguments);
}...
Now the must find a way to have this new class instancied. This done is the requery method of the Attachements class which call the 'static' ou 'class method' get of Query. In order to create a query based on Query1, the get method has had to be duplicated creating an Query1 object
get1: (function(){
/**
* #static
* #type Array
*/
var queries = [];
/**
* #returns {Query}
*/
return function( props, options ) {
var args = {},
orderby = Query1.orderby,
defaults = Query1.defaultProps,
query,
cache = !! props.cache || _.isUndefined( props.cache );
// Remove the `query` property. This isn't linked to a query,
// this *is* the query.
delete props.query;
delete props.cache;
// Fill default args.
_.defaults( props, defaults );
// Normalize the order.
props.order = props.order.toUpperCase();
if ( 'DESC' !== props.order && 'ASC' !== props.order ) {
props.order = defaults.order.toUpperCase();
}
// Ensure we have a valid orderby value.
if ( ! _.contains( orderby.allowed, props.orderby ) ) {
props.orderby = defaults.orderby;
}
_.each( [ 'include', 'exclude' ], function( prop ) {
if ( props[ prop ] && ! _.isArray( props[ prop ] ) ) {
props[ prop ] = [ props[ prop ] ];
}
} );
// Generate the query `args` object.
// Correct any differing property names.
_.each( props, function( value, prop ) {
if ( _.isNull( value ) ) {
return;
}
args[ Query1.propmap[ prop ] || prop ] = value;
});
// Fill any other default query args.
_.defaults( args, Query1.defaultArgs );
// `props.orderby` does not always map directly to `args.orderby`.
// Substitute exceptions specified in orderby.keymap.
args.orderby = orderby.valuemap[ props.orderby ] || props.orderby;
// Search the query cache for a matching query.
if ( cache ) {
query = _.find( queries, function( query ) {
return _.isEqual( query.args, args );
});
} else {
queries = [];
}
// Otherwise, create a new query and add it to the cache.
if ( ! query ) {
query = new wp.media.model.Query1( [], _.extend( options || {}, {
props: props,
args: args
} ) );
queries.push( query );
}
return query;
};
}()),
Note that get1 is a class method to create in the 2nd parameter of extend()
Extending Attachments - when the _requery method is called - in our case - the Attachments collection is replaced by a Query collection (which makes the actual admin-ajax call to query the attachments). In the extended _requery method, a Query1 query is created instead of a regular Query.
wp.media.model.Attachments1 = oldAttachments.extend({
_requery: function( refresh ) {
var props;
if ( this.props.get('query') ) {
props = this.props.toJSON();
props.cache = ( true !== refresh );
this.mirror( wp.media.model.Query1.get1( props ) );
}
},
});
Finally an object of type Attachment1 must be created during the library creation
wp.media.query1 = function( props ) {
return new wp.media.model.Attachments1( null, {
props: _.extend( _.defaults( props || {}, { orderby: 'date' } ), { query: true } )
});
};
and
// Extending the current media library frame to add a new tab
wp.media.view.MediaFrame.Post1 = oldMediaFrame.extend({
initialize: function(){
oldMediaFrame.prototype.initialize.apply(this, arguments);
var options = this.options;
this.states.add([
new Library({
id: 'inserts',
title: vja_params.title,
priority: 20,
toolbar: 'main-insert',
filterable: 'all',
multiple: false,
editable: false,
library: wp.media.query1( _.defaults({
type: 'image'
}, options.library ) ),
// Show the attachment display settings.
displaySettings: true,
// Update user settings when users adjust the
// attachment display settings.
displayUserSettings: true
}),
]);
},
The additional parameter is added in the _POST array and can be used in PHP code - ie in the query_attachments_args filter
public function query_attachments_args($args){
if (isset($_POST[CadTest])) {
do whatever you want...
}
return $args;
}
I solved it with:
var post_type = 'my-cpt';
var real_ajax_url = wp.ajax.settings.url;
// For uploading.
window.wp.Uploader.defaults.multipart_params.post_type = post_type;
// For querying.
wp.ajax.settings.url = real_ajax_url + '?post_type=' + post_type;
Then listen on the wp_ajax_query-attachments action for $_GET['post_type'] and listen on the admin_init hook for 'upload-attachment' !== $_POST['action'] and $_POST['post_type'] set to 'my-cpt'.
And unset/restore the values when finished with the dialog.
Related
I found very useful article on the web
for contact form 7
<?php
function ajax_cf7_populate_values() {
// read the CSV file in the $makes_models_years array
$makes_models_years = array();
$uploads_folder = wp_upload_dir()['basedir'];
$file = fopen($uploads_folder.'\make_model_year.csv', 'r');
$firstline = true;
while (($line = fgetcsv($file)) !== FALSE) {
if ($firstline) {
$firstline = false;
continue;
}
$makes_models_years[$line[0]][$line[1]][] = $line[2];
}
fclose($file);
// setup the initial array that will be returned to the the client side script as a JSON object.
$return_array = array(
'makes' => array_keys($makes_models_years),
'models' => array(),
'years' => array(),
'current_make' => false,
'current_model' => false
);
// collect the posted values from the submitted form
$make = key_exists('make', $_POST) ? $_POST['make'] : false;
$model = key_exists('model', $_POST) ? $_POST['model'] : false;
$year = key_exists('year', $_POST) ? $_POST['year'] : false;
// populate the $return_array with the necessary values
if ($make) {
$return_array['current_make'] = $make;
$return_array['models'] = array_keys($makes_models_years[$make]);
if ($model) {
$return_array['current_model'] = $model;
$return_array['years'] = $makes_models_years[$make][$model];
if ($year) {
$return_array['current_year'] = $year;
}
}
}
// encode the $return_array as a JSON object and echo it
echo json_encode($return_array);
wp_die();
}
// These action hooks are needed to tell WordPress that the cf7_populate_values() function needs to be called
// if a script is POSTing the action : 'cf7_populate_values'
add_action( 'wp_ajax_cf7_populate_values', 'ajax_cf7_populate_values' );
add_action( 'wp_ajax_nopriv_cf7_populate_values', 'ajax_cf7_populate_values' );
and some java
<script>
(function($) {
// create references to the 3 dropdown fields for later use.
var $makes_dd = $('[name="makes"]');
var $models_dd = $('[name="models"]');
var $years_dd = $('[name="years"]');
// run the populate_fields function, and additionally run it every time a value changes
populate_fields();
$('select').change(function() {
populate_fields();
});
function populate_fields() {
var data = {
// action needs to match the action hook part after wp_ajax_nopriv_ and wp_ajax_ in the server side script.
'action' : 'cf7_populate_values',
// pass all the currently selected values to the server side script.
'make' : $makes_dd.val(),
'model' : $models_dd.val(),
'year' : $years_dd.val()
};
// call the server side script, and on completion, update all dropdown lists with the received values.
$.post('/wp-admin/admin-ajax.php', data, function(response) {
all_values = response;
$makes_dd.html('').append($('<option>').text(' -- choose make -- '));
$models_dd.html('').append($('<option>').text(' -- choose model -- '));
$years_dd.html('').append($('<option>').text(' -- choose year -- '));
$.each(all_values.makes, function() {
$option = $("<option>").text(this).val(this);
if (all_values.current_make == this) {
$option.attr('selected','selected');
}
$makes_dd.append($option);
});
$.each(all_values.models, function() {
$option = $("<option>").text(this).val(this);
if (all_values.current_model == this) {
$option.attr('selected','selected');
}
$models_dd.append($option);
});
$.each(all_values.years, function() {
$option = $("<option>").text(this).val(this);
if (all_values.current_year == this) {
$option.attr('selected','selected');
}
$years_dd.append($option);
});
},'json');
}
})( jQuery );
</script>
but when i add it to me website nothing appear in the dropdowns. They are empty.
I tried to contact the blogger but there is no answer yet. Give me some advice
I copied the csv almost as the one in the post. So I think the function is mistaken... somewhere
I found the solution: in the article the file name is mistaken. It has to be "make_model_year.csv" !!!
I want to add a custom style to the wordpress tiny mce. There are tons of tutorials for just adding a simple option like "highlight" which will add a span with a "highlight" class. Like: https://torquemag.io/2016/09/add-custom-styles-wordpress-editor-manually-via-plugin/
But what I need is an option to add additional data, like if you add a link. You mark the words, hit the link button, an input for the url shows up.
What I want to achieve? A custom style "abbriation" (https://get.foundation/sites/docs/typography-base.html). The solution I'm thinking of is, the user marks the word, chooses the abbriation style, an input for the descriptions shows up. fin.
Hope you can help me out!
So I have something similar in most of my WordPress projects. I have a TinyMCE toolbar button that has a couple of fields that output a bootstrap button.
What you need to do is create your own TinyMCE "plugin" and to achieve this you need two parts:
A javascript file (your plugin)
A snippet of PHP to load your javascript (plugin) into the TinyMCE editor.
First we create the plugin:
/js/my-tinymce-plugin.js
( function() {
'use strict';
// Register our plugin with a relevant name
tinymce.PluginManager.add( 'my_custom_plugin', function( editor, url ) {
editor.addButton( 'my_custom_button', {
tooltip: 'I am the helper text',
icon: 'code', // #link https://www.tiny.cloud/docs/advanced/editor-icon-identifiers/
onclick: function() {
// Get the current selected tag (if has one)
var selectedNode = editor.selection.getNode();
// If we have a selected node, get the inner content else just get the full selection
var selectedText = selectedNode ? selectedNode.innerHTML : editor.selection.getContent();
// Open a popup
editor.windowManager.open( {
title: 'My popup title',
body: [
// Create a simple text field
{
type: 'textbox',
name: 'field_name_textbox',
label: 'Field label',
value: selectedText || 'I am a default value' // Use the selected value or set a default
},
// Create a select field
{
type: 'listbox',
name: 'field_name_listbox',
label: 'Field list',
value: '',
values: {
'value': 'Option 1',
'value-2': 'Option 2'
}
},
// Create a boolean checkbox
{
type: 'checkbox',
name: 'field_name_checkbox',
label: 'Will you tick me?',
checked: true
}
],
onsubmit: function( e ) {
// Get the value of our text field
var textboxValue = e.data.field_name_textbox;
// Get the value of our select field
var listboxValue = e.data.field_name_listbox;
// Get the value of our checkbox
var checkboxValue = e.data.field_name_checkbox;
// If the user has a tag selected
if ( selectedNode ) {
// Do something with selected node
// For example we can add a class
selectedNode.classList.add( 'im-a-custom-class' );
} else {
// Insert insert content
// For example we will create a span with the text field value
editor.insertContent( '<span>' + ( textboxValue || 'We have no value!' ) + '</span>' );
}
}
} );
}
} );
} );
} )();
Now we add and modify the below snippet to your themes functions.php file.
/functions.php
<?php
add_action( 'admin_head', function() {
global $typenow;
// Check user permissions
if ( !current_user_can( 'edit_posts' ) && !current_user_can( 'edit_pages' ) ) {
return;
}
// Check if WYSIWYG is enabled
if ( user_can_richedit() ) {
// Push my button to the second row of TinyMCE actions
add_filter( 'mce_buttons', function( $buttons ) {
$buttons[] = 'my_custom_button'; // Relates to the value added in the `editor.addButton` function
return $buttons;
} );
// Load our custom js into the TinyMCE iframe
add_filter( 'mce_external_plugins', function( $plugin_array ) {
// Push the path to our custom js to the loaded scripts array
$plugin_array[ 'my_custom_plugin' ] = get_template_directory_uri() . '/js/my-tinymce-plugin.js';
return $plugin_array;
} );
}
} );
Make sure to update the file name and path if you it's different to this example!
WordPress uses TinyMCE 4 and the documentation for this is lacking so finding exactly what you need can be painful.
This is merely a starting point and has not been tested.
Hope this helps!
EDIT
The below code should help you with the insertion of an "abbreviations" tag and title attribute.
( function() {
'use strict';
tinymce.PluginManager.add( 'my_custom_plugin', function( editor, url ) {
editor.addButton( 'my_custom_button', {
tooltip: 'Insert an abbreviation',
icon: 'plus',
onclick: function() {
var selectedNode = editor.selection.getNode();
var selectedText = selectedNode ? selectedNode.innerHTML : editor.selection.getContent();
editor.windowManager.open( {
title: 'Insert an abbreviation',
body: [
{
type: 'textbox',
name: 'abbreviation',
label: 'The abbreviated term',
value: selectedText
},
{
type: 'textbox',
name: 'title',
label: 'The full term',
value: ''
}
],
onsubmit: function( e ) {
var abbreviation = e.data.abbreviation;
var title = e.data.title.replace( '"', '\"' );
if ( selectedNode && selectedNode.tagName === 'ABBR' ) {
selectedNode.innerText = abbreviation;
selectedNode.setAttribute( 'title', title );
} else {
editor.insertContent( '<abbr title="' + title + '">' + abbreviation + '</abbr>' );
}
}
} );
}
} );
} );
} )();
I'm trying to add some items to a DynamoDB set. This worked fine with the original JavaScript SDK, but not with the new DocumentClient, using the createSet() function. Here's my code:
'use strict';
let docClient = new AWS.DynamoDB.DocumentClient({
region: 'us-east-2',
accessKeyId: 'AKIAJWIR35J4YZF4RQVQ',
secretAccessKey: 'xxxx'
});
var params = {
TableName : 'qa_Web_Application',
Key: {'Application_ID': '78f27a00-11f6-49cc-9adb-ae0795cf79d4'},
UpdateExpression : 'ADD #idList :newIds',
ExpressionAttributeNames : {
'#idList' : 'ids'
},
ExpressionAttributeValues : {
':newIds' : docClient.createSet([1,2])
}
};
console.log( params.ExpressionAttributeValues[":newIds"] );
docClient.update(params, function(err,data) {
if( err !== null ) {
console.log( err, err.stack );
} else {
console.log ( data );
}
});
Here's the output:
constructor {values: Array(2), type: "Number"}
Error: Invalid UpdateExpression: Incorrect operand type for operator or function; operator: ADD, operand type: MAP
I've seen this same question here (How do you update a Set on DynamoDB using JavaScript document client?), but that's what I'm basing this code example on, and it fails.
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 attempted implementing search in Telescope using pure javascript, since it looks like FTS is a while off for Meteor to implement and I couldn't get 2.4 playing nicely with Meteor yet.
I'm using the existing pagination model that is already implemented in Telescope to display the Top/New/Best posts, plus a Session variable for the search keyword that is set in the Router when you navigate to e.g. /search/foobar.
However, it doesn't quite seem to be working; when I have, say, 100 posts, the regular paginated subscription only comes back with 25 of these and my search results only show the posts in the first 25.
I've been banging my head against a wall for days trying to debug this one: sometimes it works, sometimes it doesn't!
Here's the code (I've included all additional search code for reference):
app.js:
var resultsPostsSubscription = function() {
var handle = paginatedSearchSubscription( 10, 'searchResults' );
handle.fetch = function() {
return limitDocuments( searchPosts( Session.get( 'keyword' ) ), handle.loaded() );
};
return handle;
};
var resultsPostsHandle = resultsPostsSubscription();
paginated_sub.js:
I duplicated the existing paginatedSubscription because I can't pass a Session var in as an arg; it needs to be dynamic. I'll probably refactor later.
paginatedSearchSubscription = function (perPage/*, name, arguments */) {
var handle = new PaginatedSubscriptionHandle(perPage);
var args = Array.prototype.slice.call(arguments, 1);
Meteor.autosubscribe(function() {
var subHandle = Meteor.subscribe.apply(this, args.concat([
Session.get( 'keyword' ), handle.limit(), function() { handle.done(); }
]));
handle.stop = subHandle.stop;
});
return handle;
}
search.js: (new file, in /common directory)
// get all posts where headline, categories, tags or body are LIKE %keyword%
searchPosts = function( keyword ) {
var query = new RegExp( keyword, 'i' );
var results = Posts.find( { $or: [ { 'headline': query }, { 'categories': query }, { 'tags': query }, { 'body': query } ] } );
return results;
};
publish.js:
Meteor.publish( 'searchResults', searchPosts );
posts_list.html:
<template name="posts_results">
{{> posts_list resultsPostsHandle}}
</template>
posts_list.js:
Template.posts_results.resultsPostsHandle = function() {
return resultsPostsHandle;
};
router.js:
there's a search bar in the nav that redirects to here
posts_results = function( keyword ) {
Session.set( 'keyword' , keyword );
return 'posts_results';
};
Meteor.Router.add({
...
'/search/:keyword':posts_results,
...
})
Any help would be greatly appreciated!
A little late but here is a full write up on how to implement full text search in meteor.
"The simplest way without editing any Meteor code is to use your own mongodb."