I've been coding in PHP for a long time, but I'm currently writing my first Wordpress plugin. The plugin's goals are:
Display a form on a public-facing page to collect simple data from
site visitors (first/last name, etc)
Provide a way for admins export the data
I've got a plugin that successfully creates a table on activation and a shortcode that provides a form which successfully stores the submitted data in the database.
On the back-end, I have a dashboard widget that currently displays some stats about the submissions, and my last task is to provide a button to export those stats to CSV, and that's where I'm stumped. I'm not sure how to handle this in WP world...in the past, I would have had the button open a new window to a page that does the exporting and echos a CSV string to the page along with headers that indicate it's a binary file so it's downloaded. In WP, how do I accomplish this? Do I put a PHP script in my plugin directory and have my widget open that page? If so, how does that page gain access to $wpdb to handle the data access?
Here is my code (just for the dashboard widget part) as it stands now:
<?php
/*
Plugin meta details
*/
add_action('init', 'myplugin_buffer_start');
add_action('wp_footer', 'myplugin_buffer_end');
function myplugin_ob_callback($buffer) {
// You can modify buffer here, and then return the updated code
return $buffer;
}
/**
* Action: init
* Runs after WP has finished loading but before any headers are sent, user is already authenticated at this point
* Good for intercepting $_POST/$_GET
*/
function myplugin_buffer_start()
{
ob_start("myplugin_ob_callback");
}
/**
* Action wp_footer
* Triggered near the </body> tag of the user's template by the wp_footer() function.
*/
function myplugin_buffer_end()
{
ob_end_flush();
}
/****************************************************************
* Stats Dashboard Widgets
***************************************************************/
function myplugin_displaytestFormWidget_process()
{
$errors = array();
if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset ( $_POST['myplugin_export_button'] ))
{
ob_end_clean(); // erase the output buffer and turn off buffering...blow away all the markup/content from the buffer up to this point
global $wpdb;
$tableName = $wpdb->prefix . "myplugin_test_form";
$qry = "select Name, Date from $tableName order by Date desc";
//ob_start(); when I uncomment this, it works!
$result = $wpdb->get_results($qry, ARRAY_A);
if ($wpdb->num_rows)
{
$date = new DateTime();
$ts = $date->format("Y-m-d-G-i-s");
$filename = "myCsvFile-$ts.csv";
header( 'Content-Type: text/csv' );
header( 'Content-Disposition: attachment;filename='.$filename);
$fp = fopen('php://output', 'w');
//$headrow = $result[0];
//fputcsv($fp, array_keys($headrow));
foreach ($result as $data) {
fputcsv($fp, $data);
}
fclose($fp);
//when I uncomment these lines along with adding ob_start above, it works
//$contLength = ob_get_length();
//header( 'Content-Length: '.$contLength);
}
}
return myplugin_displaytestFormWidget();
}
function myplugin_displaytestFormWidget()
{
global $wpdb;
$tableName = $wpdb->prefix . "myplugin_test_form";
$submissionCount = $wpdb->get_var("select count(Id) from $tableName");
?>
<div><strong>Last entry: </strong>John Q. test (May 5, 2013)</div>
<div><strong>Total submissions: </strong> <?php echo $submissionCount ?></div>
<form id="myplugin_test_export_widget" method="post" action="">
<input type="submit" name="myplugin_export_button" value="Export All" />
</form>
<?php
}
function myplugin_addDashboardWidgets()
{
// widget_id, widget_name, callback, control_callback
wp_add_dashboard_widget(
'test-form-widget',
'test Form Submissions',
'myplugin_displaytestFormWidget_process'
);
}
/****************************************************************
* Hooks
***************************************************************/
//add_action('widgets_init', 'simple_widget_init');
add_action('wp_dashboard_setup', 'myplugin_addDashboardWidgets' );
// This shortcode will inject the form onto a page
add_shortcode('test-form', 'myplugin_displaytestForm_process');
register_activation_hook(__FILE__, 'myplugin_test_form_activate');
You can see in the myplugin_displayTestFormWidget function I'm displaying the form, I just don't know what to do with the button to make it all jive.
Can anyone assist?
At first add following code in your plugin
add_action('init', 'buffer_start');
add_action('wp_footer', 'buffer_end');
function callback($buffer) {
// You can modify buffer here, and then return the updated code
return $buffer;
}
function buffer_start() { ob_start("callback"); }
function buffer_end() { ob_end_flush(); }
Just at the top, right after the plugin meta info like
/**
* #package Word Generator
* #version 1.0
* ...
*/
// here goes the code given above, it'll solve the header sent error problem
And following code will dump a csv file
if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset ( $_POST['myplugin_export_button'] ))
{
// Somehow, perform the export
ob_clean();
global $wpdb;
$qry = 'your query';
$result = $wpdb->get_results($qry, ARRAY_A);
if ($wpdb->num_rows){
$date = new DateTime();
$ts = $date->format("Y-m-d-G-i-s");
$filename = "myCsvFile-$ts.csv";
header( 'Content-Type: text/csv' );
header( 'Content-Disposition: attachment;filename='.$filename);
$fp = fopen('php://output', 'w');
$headrow = $result[0];
fputcsv($fp, array_keys($headrow));
foreach ($result as $data) {
fputcsv($fp, $data);
}
fclose($fp);
$contLength = ob_get_length();
header( 'Content-Length: '.$contLength);
exit();
}
}
I've implemented similar functionality in another plugin I developed a while ago. I won't claim it's the best practice (I'm not 100% sure if there is such a thing in this instance) but it seemed like a clean and reliable solution for me at the time.
Picking up from where you left off, inside your myplugin_displayTestFormWidget_process function, let me just put some real and pseudo code that should get you rolling.
if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset ( $_POST['myplugin_export_button'] ))
{
// clear out the buffer
ob_clean();
// get the $wpdb variable into scope so you may use it
global $wpdb;
// define some filename using a timestamp maybe
// $csv_file_name = 'export_' . date('Ymd') . '.csv';
// get the results of your query
$result = $wpdb->get_results("SELECT * FROM your_table");
// loop your results and build your csv file
foreach($result as $row){
// put your csv data into something like $csv_string or whatever
}
header("Content-type: text/x-csv");
header("Content-Transfer-Encoding: binary");
header("Content-Disposition: attachment; filename=".$csv_file_name);
header("Pragma: no-cache");
header("Expires: 0");
echo $csv_string;
exit;
}
You mentioned you are pretty comfortable with PHP so I didn't really dig into the PHP aspects of getting this done.
Hope that helps, have fun!
Related
I have a custom checkbox field created in Wordpress where the user can select various facilities available at a listed property.
I am building a page template using Elementor.
When I import the data from ACF, it displays as a comma separated list.
Is there any way I can instead get this to display as as a bulleted list?
Here is a code to get the output data from ACF as a bulleted list -
<?php
$arr = get_the_field('field_name');
$str = explode (",", $arr);
echo '<ul>';
foreach($str as $s){
echo '<li>'.$s.'</li>';
}
echo '</ul>';
?>
I hope this is what you are looking for.
You could try using a plugin that let's you create php code snippets and run them with a shortcode such as this: https://it.wordpress.org/plugins/insert-php/
Once you created the php snippet you could try to run it with a shortcode using Elementor's shortcode widget.
I'd slightly adjust Tahmid's excellent answer. In order to allow for empty lines (without a bullet) use this in you functions.php file:
/**
* create a ACF text field with bullets
* Empty lines stay empty
* #param string $field_name Name of the field to bullet
*/
function acf_bullets(string $field_name): void {
$format_acf_bullet = function(
string $value,
int $post_id,
array $form ):string {
if( empty($value) ) {
return '';
}
$lines = explode( "\n", $value );
$result = "<ul class=\"theme-ul\">\n";
foreach( $lines as $line) {
if( strlen($line)<=1 ) { // empty line, start a new list
$result .= "</ul><p/><ul class=\"theme-ul\">\n";
} else {
$result .= "<li>".$line."\n";
}
}
$result .= "</ul>\n";
return $result;
};
add_filter("acf/format_value/name=$field_name", $format_acf_bullet, 10, 3);
}
Call this with acf_bullets('your-fieldname-here');
I want to export a Wordpress frontend search result to Excel file.
I have created a data management site using Wordpress, and used those plugins:
Custom Post Type (CPT UI): to have a dedicated section for post management
Advanced Custom Field (ACF): to enter data forms, attached to that post
Search & Filter Pro: to filter search result, based on above ACF form values.
Elementor Pro: to display "frontend search result", using Loop Template and Search & Filter shortcode.
Is there anyway user can export this Frontend Search Result to an EXCEL file, for download?
Thanks so much for your help.
I'll start this off in a direction, and we can refine it together, if needed.
You can easily create a CSV file (which users can open with Excel):
$array = [ ["letters","numbers"], ["a",3], ["b",4] ];
/* Could be $_POST or something else */
function create_csv( $array ) {
/* Container */
$output = '';
foreach( $array as $data ){
$count = count($data); $c=0;
foreach( $data as $d ){
$c++;
$output .= $d;
if( $c !== $count ) {
$output .= ','; /* cell break */
} else {
$output .= '\n'; /* row break */
}
}
}
/* Build form array */
$array = array(
'array' => $output,
);
/* check if the file exists or not to determine if headers are needed */
$headersNeeded = !file_exists('filename.csv');
/* Open or Create CSV File */
$fh = fopen('filename.csv', 'a');
/* if headers are needed, add them */
if ($headersNeeded){
fputcsv($fh, $headers);
}
/* Populate the array */
fputcsv($fh,$array);
/* Close the file */
fclose($fh);
}
If you can identify a way to 'read' the search results into such a function, and you design a method for identifying the unique file name on page render, you could use HTML5 button attributes to trigger the download:
Download
I am trying to build a plugin where I need to take inputs from users from a form and create a shortcode with some of the user inputs. I have created a separate page for the adding form. there I am trying to process the input data and save on a custom table in WordPress DB. The problem I am facing is that, After saving the data in my custom table, I want to create a shortcode with some of the inputs but the add_shortcode method with its callback function is not working. I am new to Wordpress so I am not sure where I am going wrong.
I have tried a lot of ways but at the end was left with scratching my head. I have tried first saving everything. Then on the plugin main file. Tried to retrieve data from DB and create shortcode there. But no result
if (isset($_POST['sc_add_submit'])) {
$name = sanitize_text_field($_POST['sc_name']);
$shortcode = sanitize_text_field($_POST['shortcode']);
$content = sanitize_text_field($_POST['content']);
$short_desc = sanitize_text_field($_POST['short_desc']);
$errors = [];
$msgs = [];
global $wpdb;
$wpdb->insert('wp_sc_content', array(
'sc_name' => $name,
'shortcode' => 'sc_'.$shortcode,
'content' => $content,
'short_desc' => $short_desc,
));
$sc_lastInsert_id = $wpdb->insert_id;
if (!empty($sc_lastInsert_id)) {
$msgs[] = "Shortcode inserted succesfully";
function sc_register_shortcode()
{
return $content;
}
add_shortcode($shortcode, 'sc_register_shortcode');
} else {
$errors[] = "DB insert failed";
}
}
I just need to register the shortcode each time the form is submitted. So that the user paste the shortcode and it returns the content wherever he wants.
So, your shortcodes are "added" as soon as the form is submitted. When you navigate to other pages or reload the screen, the add_shortcode() is not triggering anymore!
What you need to do is to query your shortcodes from the database and hook it to something like init, so it's always loaded.
Let me write the code for you-
<?php
add_action( 'init', 'wpse_54302608_add_shortocodes' );
function wpse_54302608_add_shortocodes() {
global $wpdb;
$table = 'wp_sc_content'; // hopefully $wpdb->prefix . 'sc_content';
$query = "SELECT * FROM {$table}";
$results = $wpdb->get_results( $query );
foreach ( $results as $row ) {
$fn = function() use ( $row ) {
return $row->content;
};
add_shortcode( $row->shortcode, $fn );
}
}
Not tested. But should work.
I am using the FeedWordPress plugin http://wordpress.org/extend/plugins/feedwordpress/ to pull posts from one site to another.
I have written a filter that after some help from Stack users successfully scans the $content and extracts the image URL into $new_content
define('FWPASTOPC_AUTHOR_NAME', 'radgeek');
add_filter(
/*hook=*/ 'syndicated_item_content',
/*function=*/ 'fwp_add_source_to_content',
/*order=*/ 10,
/*arguments=*/ 2
);
function fwp_add_source_to_content ($content, $post) {
// Use SyndicatedPost::author() to get author
// data in a convenient array
$content = $post->content();
// Authored by someone else
if( preg_match( '/<img[^>]+src\s*=\s*["\']?([^"\' ]+)[^>]*>/', $content, $matches ) ) {
$new_content .= 'URL IS '.$matches[0].'';
return $new_content;
}
else
{
}
}
What I wanted to do now was save this URL into a custom field instead of just returning it. Has anyone achieved anything similar?
So as I understand it, the plugin grabs content from external RSS feeds and creates them as posts in your website.
If this is the case, using your filter you should be able to grab the post ID within the $post variable.
So all you need is the add_post_meta() function to add a custom field to the specific post.
So including your code above it should look something like:
define('FWPASTOPC_AUTHOR_NAME', 'radgeek');
add_filter(
/*hook=*/ 'syndicated_item_content',
/*function=*/ 'fwp_add_source_to_content',
/*order=*/ 10,
/*arguments=*/ 2
);
function fwp_add_source_to_content ($content, $post) {
// Use SyndicatedPost::author() to get author
// data in a convenient array
$content = $post->content();
// Authored by someone else
if( preg_match( '/<img[^>]+src\s*=\s*["\']?([^"\' ]+)[^>]*>/', $content, $matches ) ) {
$new_content .= 'URL IS '.$matches[0].'';
//Add custom field with author info to post
add_post_meta($post->ID, 'post_author', $new_content);
return $new_content;
}
}
I have a wordpress site that connects to a soap server. The problem is every time I run the script the wp_insert_post is using the same result again.
I would like to check if existing post_title matches the value from $title then if they match, prevent wp_insert_post from using the same value again.
Here's the code:
try {
$client = new SoapClient($wsdl, array('login' => $username, 'password' => $password));
} catch(Exception $e) {
die('Couldn\'t establish connection to weblink service.');
}
$publications = $client->GetPublicationSummaries();
foreach ($publications->GetPublicationSummariesResult->PublicationSummaries->PublicationSummary as $publication_summary) {
// get the complete publication from the webservice
$publication = $client->getPublication(array('PublicationId' => $publication_summary->ID))->GetPublicationResult->Publication;
// get all properties and put them in an array
$properties = array();
foreach ($publication->Property as $attribute => $value) {
$properties[$attribute] = $value;
}
// Assemble basic title from properties
$title = $properties['Address']->Street . ' ' . $properties['Address']->HouseNumber . $properties['Address']->HouseNumberExtension . ', ' . $properties['Address']->City->_;
}
$my_post = array(
'post_title'=>$title,
'post_content'=>'my contents',
'post_status'=>'draft',
'post_type'=>'skarabeepublication',
'post_author'=>1,
);
wp_insert_post($my_post);
Thank you for any help.
You can use get_page_by_title() as it supports custom post types now.
if (!get_page_by_title($title, OBJECT, 'skarabeepublication')) :
$my_post = array(
'post_title'=>$title,
'post_content'=>'my contents',
'post_status'=>'draft',
'post_type'=>'skarabeepublication',
'post_author'=>1,
);
wp_insert_post($my_post);
endif;
Codex information here
Surprised not to see mention of post_exists function in wp-includes/post.php. See entry on wpseek. There is no entry in the codex. At it's simplest it works like get_page_by_title but returns a post id (or 0 if not found) instead of the object (or null).
$post_id = post_exists( $my_title );
if (!$post_id) {
// code here
}
Sorry for the late response. I used what Robot says in the comment and this solved my problem. Thanks
$post_if = $wpdb->get_var("SELECT count(post_title) FROM $wpdb->posts WHERE post_title like '$title_from_soap'");
if($post_if < 1){
//code here
}
sampler:
if( !get_page_by_path('mypageslug',OBJECT,'post') ){
//your codes
}