dryriver writes: BBC Capital explores why good ideas people have in the workplace almost never reach the top decision-makers in a company. From the report: "Surely you’ve heard the plea from on high at your company: we want more innovation, from everyone at every level. Your boss might even agree with the sentiment — because, of course, who doesn’t like innovation? It’s good for everyone, right? Yet when it comes to innovating at your job it might be better to lower your expectations — and then some. Your idea is far more likely to die on your boss’s desk than it is to reach the CEO. It’s not that top managers don’t want new ideas. Rather, it’s the people around you — your colleagues, your manager — who are unlikely to bend toward change. Today, big companies that don’t innovate face extinction. ‘Companies are almost forced to say that they are changing these days,’ says Lynn Isabella, professor of organizational behavior at the University of Virginia Darden School of Business in the U.S. But, ‘it’s not organizations that resist change; people resist,’ says Isabella. ‘The people have to see what’s in it for them.’" As mentioned in the report, some of the key questions that the people whom you pitch your ideas to will ask themselves include, what does this innovation mean for me personally — will it be more challenging or will it lead to more career opportunities, and what will it mean for my job — will I get fired or will it be (or was it) worth it? Many times the answers to these questions don’t stack up in favor of the innovation, Isabella says. As a result, the people who need to buy in don’t push for change.
Tonight at 9 p.m. EST, 6 p.m. pacific, Trump will be giving his first major speech to a joint session of Congress. Here’s how you can watch it anywhere, no cable required.
The address will be streaming live through all of these outlets:
The Onion (if you want your address with commentary)
Trump’s speech isn’t technically a State of the Union address since that usually doesn’t happen until a president has been in office for a year. But the so-called “address to a joint session” is still pretty important, with the president expected to lay out his vision and goals for the country in the coming years.
Let’s briefly touch on how Elasticsearch and MongoDB became easy targets…
Elasticsearch
Elasticsearch® does not implement any access control: neither authentication nor authorization. For this, you need to deploy the Elastic’s shield offering. As such, if you have an Elasticsearch deployment that is addressable from the Internet, you’re asking for trouble. We see many deployments have some authentication around their access, such as HTTP Basic Auth – though sadly, some don’t employ authentication or network isolation. We already wrote a blog about this here.
MongoDB
MongoDB (< 2.6.0) does allow for access control through account creation. It binds to
0.0.0.0
by default (allowing access from anywhere). This is now changed in /etc/mongod.conf in versions >= 2.6.0. Often administrators don’t realize or don’t know to look for this. (Using MongoDB? My colleague David Murphy wrote a post on this issue here).
We began to see incidents where both Elasticsearch and MongoDB had their datasets removed and replaced with a
README/note
instructing the user to pay a ransom of 0.2BTC (Bitcoin) to the specified wallet address (if they wanted their data back).
MySQL
So is this latest (and similar) attack on MySQL MySQL’s fault? We don’t think so. MySQL and Percona Server® for MySQL by default do not accept authentication from everywhere without a password for the
root
user.
Let’s go over the various security options MySQL has, and describe some other best practices in order to protect your environment.
Default
bind_address=127.0.0.1
in Percona Server for MySQL
MySQL currently still binds to
0.0.0.0
(listen to all network interfaces) by default. However, Percona Server for MySQL and Percona XtraDB Cluster have different defaults, and only bind on
Recall, if you will, CVE-2012-2122. This ALONE should be enough to ensure that you as the administrator use best practices, and ONLY allow access to the MySQL service from known good sources. Do not setup root level or equivalent access from any host (
%
indicates any host is allowed). Ideally, you should only allow root access from
127.0.0.1
– or if you must, from a subset of a secured network (e.g.,
10.10.0.%
would only allow access to
10.10.0.0/24
).
Prevent Access
Also, does the MySQL database really need a publicly accessible IP address? If you do have a valid reason for this, then you should firewall port 3306 and whitelist access only from hosts that need to access the database directly. You can easily use
iptables
for this.
Default Users
MySQL DOES NOT by default create accounts that can be exploited for access. This comes later through an administrator’s lack of understanding, sadly. More often than not, the grant will look something like the following.
GRANT ALL PRIVILEGES TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
You may scoff at the above (and rightly so). However, don’t discount this just yet: “123456” was the MOST USED password in 2016! So it’s reasonable to assume that somewhere out there this is a reality.
Max Connection Errors
You can deploy max_connection_errors with a suitably low value to help mitigate a direct attack. This will not prevent a distributed attack, where many thousands of hosts are used. Network isolation is the only way to ensure your mitigation against this attack vector.
MySQL 5.7 Improvements on Security
Default Root Password
Since MySQL 5.7, a random password is generated for the only root user (
root@localhost
) when you install MySQL for the first time. That password is then written in the error log and has to be changed. Miguel Ángel blogged about this before.
Connection Control Plugin
MySQL 5.7.17 introduced a new open source plugin called Connection Control. When enabled, it delays the authentication of users that failed to login by default more than three times. This is also part as of Percona Server for MySQL 5.7.17.
Here’s an example where the 4th consecutive try caused a one-second delay (default settings were used):
$ time mysql -u bleh2 -pbleh
ERROR 1045 (28000): Access denied for user 'bleh2'@'localhost' (using password: YES)
real 0m0.009s
$ time mysql -u bleh2 -pbleh
ERROR 1045 (28000): Access denied for user 'bleh2'@'localhost' (using password: YES)
real 0m0.008s
$ time mysql -u bleh2 -pbleh
ERROR 1045 (28000): Access denied for user 'bleh2'@'localhost' (using password: YES)
real 0m0.008s
$ time mysql -u bleh2 -pbleh
ERROR 1045 (28000): Access denied for user 'bleh2'@'localhost' (using password: YES)
real 0m1.008s
mysql> SELECT * FROM INFORMATION_SCHEMA.CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS;
+---------------------+-----------------+
| USERHOST | FAILED_ATTEMPTS |
+---------------------+-----------------+
| 'bleh2'@'localhost' | 4 |
+---------------------+-----------------+
1 row in set (0.01 sec)
Password Validation Plugin
MySQL 5.6.6 and later versions also ship with a password validation plugin, which prevents creating users with unsafe passwords (such as
In order to get stung, one must ignore the best practices mentioned above (which in today’s world, should take some effort). These best practices include:
Don’t use a publicly accessible IP address with no firewall configured
Don’t use a
root@%
account, or other equally privileged access account, with poor MySQL isolation
Don’t configure those privileged users with a weak password, allowing for brute force attacks against the MySQL service
Hopefully, these are helpful security tips for MySQL users. Comment below!
"The Pentagon is the latest government entity to join the open-source movement," writes NextGov. An anonymous reader quotes their report:
The Defense Department this week launched Code.mil, a public site that will eventually showcase unclassified code written by federal employees. Citizens will be able to use that code for personal and public projects… The Defense Department’s Digital Service team, whose members are recruited for short-term stints from companies including Google and Netflix, will be the first to host its code on the site once the agreement is finalized… "This is a direct avenue for the department to tap into a worldwide community of developers to collectively speed up and strengthen the software development process," a DOD post announcing the initiative said. The Pentagon also aims to find software developers and "make connections in support of DOD programs that ultimately service our national security." Interestingly, there’s no copyright protections on code written by federal employees, according to U.S. (and some international) laws, according to the site. "This can make it hard to attach an open source license to our code, and our team here at Defense Digital Service wants to find a solution. You can submit a public comment by opening a GitHub issue on this repository before we finalize the agreement at the end of March."
David Goodman, director of the Ohio Development Services Agency, says the state has a panoply of programs helping small businesses that don’t get the same publicity as a tax credit for a huge corporation adding 1,000 jobs.
For example, Columbus e-commerce web developer Rocket Code LLC can earn up to $210,000 in incentives if it creates 30 jobs and meets other Ohio Tax Credit Authority Terms. The company applied for the credit when it had just 18 people, and unlike some state programs, it didn’t…
A young, Silicon Valley venture firm is taking the wraps off a piece of software today that it says makes it a cinch for founders to figure out which VCs are worth approaching, based on stage, sector, and a variety of other factors.
It’s called Signal, and it’s first project of NFX Guild Labs, an offshoot of the venture firm and invite-only accelerator program NFX Guild, which we’ve written about here.
Serial entrepreneur James Currier — who cofounded NFX with longtime business partners Gigi Levy Weiss and Stan Chudnovsky (who’s also the head of product at Facebook Messenger) — says that like so many innovations, Signal comes from a problem he found himself struggling to address.
Though part of NFX Guild’s promise to founders is to help them navigate the fundraising process, he says there was “no place for us to get an easy, clean list of active investors, where we could choose our target investors, then export that into a Google Doc or whatever.”
Signal is focused first and foremost for founders, but it should prove useful for VCs, too, says Currier. He uses travel startups as a theoretical example. “For [the early-stage venture firm] Felicis Ventures, a related intro is [a waste of everyone’s time]. Felicis doesn’t invest in travel, but no one knows that and there’s no easy way for Felicis to signal that to everyone. Or,” adds Currier, “say you’re an investor and you haven’t really spent time with blockchain startups. You won’t pop up on a founder’s radar as a result, but if you say on Signal that you’re starting to [poke around], you will.”
It does seem fairly straightforward, for what it’s worth. You simply connect your Gmail or sign in separately with a user name and password and start searching and filtering through 4,500 investors in mostly Silicon Valley and Israel whose profiles have already been ported into the platform. (Currier and his team created these snapshots themselves; he says investors can now go in and tweak or add to their bios or else join the platform if they don’t seem themselves listed.) Afterward, you collect a short list of your target investors, then export the list to a spreadsheet or whatever tool you’re using to manage your fundraising process.
Current members and alums of NFX Guild have already been using the platform for months. Currier says that 94 percent of them choose to connect their Gmail because the platform can then rank which of the founders’ contacts have the strongest relationships to the specific venture capitalists they want to talk with. (Currier notes that “we don’t look at any email stuff; we’re just looking at metadata.”)
After the founder zeroes in on which of his or her contacts have the closest relationship to the VCs they need to talk with, they can ask for — and hopefully receive — an introduction the old-fashioned way: via email.
Currier calls what NFX is releasing today “version 1.0,” explaining that in the future, founders will be able to land an introduction directly through the platform. They’ll also be able to send their own private and secure company summary, making it easy for investors to evaluate whether there’s a fit. In fact, there’s already a “Signal Elite” version of the platform, but it’s only available to NFX Guild founders and companies at the moment.
One of them is Sarah Schaaf, an attorney who spent a year with Google before launching Headnote, a workflow startup aiming to help attorneys get paid faster, close more clients and collaborate more efficiently.
Schaaf went through the NFX program last spring and says she “had an incredible reaction” after the outfit’s demo day but wasn’t ready to fundraise until late last year. The problem: as someone who “grew up in a family of attorneys,” she said she could figure out who the “best 20 VCs are” but finding out who the best VCs for her startup’s stage, area, and particular financing needs was becoming a “total nightmare” that involved poring over LinkedIn, Crunchbase, and CB Insights, among other data sources. With Signal, she says, she quickly zeroed in on SoftTech VC, which recently led a $2.5 million round in her company. “I could tell on a scale of 1 to 10 who to ask” for an introduction. “It cut right through the noise.”
By the way, if you’re a VC, you might be wondering if Currier and company will be expanding Signal to include a way for you to find the right institutional investors when it comes time for your own fundraising. Currier says it isn’t high on NFX’s list of priorities, but he’s definitely hearing from investor friends who wish it were otherwise, adding with a laugh, “We might end up going there.”
FOSSA wants to help developers manage the tricky terrain of open source license management, and today it announced a $2.2 million seed round. The company also announced that its license management product by the same name was available in open Beta.
Let’s start with the funding, which was led by Bain Capital Ventures along with an all-star list of participants including Salesforce Chairman and CEO Marc Benioff, former YouTube CTO/co-founder Steve Chen, former Skype CTO/co-founder Jaan Tallinn, former Cloudera CTO/co-founder Amr Awadallah and Tinder CMO/co-founder Justin Mateen.
It seems that these folks recognize that this company is attempting to solve a serious problem around open source license management. FOSSA’s 22-year old founder, Kevin Wang, says he today’s programs tend to be made up of a series of open source and third-party components, each with its own unique attribution requirements. Trying to keep up with this has been a daunting task for developers, and there has been a dearth of solutions. In fact, most people use a spreadsheet to track licensing requirements manually, Wang explained.
“It’s 2017 and we don’t know what we are shipping in production. Developers don’t have control of their code,” he said.
His product is supposed to solve that problem by analyzing the code in an entirely automated fashion, finding the license requirements, and offering fixes when a problem is found (integrating the recommendations into trackers like Jira or even communications tools like Slack). It includes proper legal language (written by open source lawyers, according to Wang) and it also automates all disclosures and attributions.
Photo: FOSSA
Salil Deshpande, who is managing director at lead investor Bain Capital Ventures says this was the only enterprise-grade solution of its kind he has seen. “Modern development trends are adding profound speed and risk to software development … automated license management is no longer just nice to have, it’s dangerous to not have it,” he said in a statement.
That’s because there are legal implications for failing to provide the proper attribution in the code. While, Wang wouldn’t say his solution was fool-proof, he said it gets development teams closer to full compliance than trying to do it manually, a nearly impossible task for a modern application with hundreds of plug-ins and libraries. “At the end of the day the responsibility lies with the customer, but we are offering a way to get as compliant as possible with the least effort,” Wang said.
The company launched in 2014, and has under 10 employees today. Wang plans to use the seed money to expand engineering, sales and go-to market efforts.
In this tutorial, I’m going to walk you through how you can add a new menu in WordPress Admin Area, where your users will be able to import any demo content — including widgets, their positions and navigation as well — by a single click. The code follows the best WordPress practices, uses WP Filesystem for file management, includes escaping and all text strings are prepared for translation. It also passes the WordPress theme check plugin!
The script in action…
Important note: this script will remove existing posts, pages, comments and media library from the database! So it’s recommended to use the script on a new WordPress install or demo/testing site. The script is meant to give you a starting point to build your own content importer and customize it as desired. Here is what our script will accomplish in a nutshell:
Check webhosting settings, throw error message if something is not okay
Copy demo media files into the upload folder
Clear current posts, pages, comments and media library from the database
Import demo posts, pages, comments and media library into the database
Configure navigation, widgets and other settings
You can download the completed source files if you want follow along with the tutorial. I assume that non-beginner coders will read this article so I won’t explain every line. Let’s begin!
Preparing your demo content
So probably at this point your theme is finished already, filled with demo content and located on your local webserver, along with a new or test installation of WordPress. In this tutorial we’ll use the following path as reference to your finished theme folder: http://localhost/cooltheme/
Make sure that your uploaded media files are not stored in month- and year-based folders in the http://localhost/cooltheme/wp-content/uploads/ directory. This is important because we don’t want to mess with those folders when we copy the demo content. You can verify this setting in the WP Admin Area, under Settings ▸ Media ▸ Uploading files. Here is a screenshot of what that setting should look like:
In case you have some files that are organized in month and year folders, disable the “Uploading files” setting and save the changes. After that unfortunately you’ll have to go through the painful process of re-adding the files to the root of the /uploads/ directory. Basically for this tutorial, all media should be located in /uploads/ without using any year/month subdirectories.
Preparing the media files
Within your current theme’s directory, http://localhost/cooltheme/wp-content/themes/cooltheme/, create a folder named /importer/. Within that folder, create a folder named /media/. When finished, your theme should include the following directories:
Once that is set up, copy all files from your WP /wp-content/uploads/ directory, and paste them into the /media/ folder in your theme. Tip: if you are planning to distribute your theme publicly, make sure that all of the media files are properly licensed.
Exporting database content
Now let’s export all the posts, comments, and pages from the database. I’m going to use phpMyAdmin for this, since it’s the most popular MySQL management tool out there. Obviously you can also use your own preferred tool, e.g. SQLyog, etc. Log in to your theme’s database via phpMyAdmin and follow these steps:
Go to the Export tab
Select the Custom export method
Select these tables: wp_comments, wp_postmeta, wp_posts, wp_terms, wp_term_relationships, and wp_term_taxonomy
Save the output to a file with character set UTF-8, no compression
Set the format as: SQL
At format-specific options select data only (if there’s an option for this), we won’t need the structure
Function to use when dumping data: INSERT
Syntax to use when inserting data: insert multiple rows in every INSERT statement
Press the Go button and save this SQL dump file on your local machine
Now open the exported database file in your favorite text editor (I prefer Notepad++). Also create a new file, set its encoding to UTF-8, and save it as data.imp in your theme’s new /importer/ directory. This file will contain SQL data for insert.
Now copy all INSERT statements from the SQL dump and paste them below each other in data.imp. Make sure you copy-paste only the statements, leave comments and everything else behind. Finally put a unique separator among all statements, for example: <cooltheme_sep>, and remove all line breaks between the statements. This will help us to properly process SQL data later. Here is an image how your data.imp content should look something like this:
Add the Importer menu to the WP Admin Area
Now that the demo content is prepared, let’s code the Importer! Open your theme’s functions.php file and add the following code:
if (is_admin()) {
include_once('importer/importer.inc.php');
}
Create a new file in your code editor, name it importer.inc.php, save it in the /importer/ directory. Then insert these code lines:
<?php
//add importer to WP admin
add_action('admin_menu', 'YOURPREFIX_add_demo_importer');
function YOURPREFIX_add_demo_importer() {
add_theme_page(esc_html__('Demo Importer', 'cooltheme'), esc_html__('Demo Importer', 'cooltheme'), 'administrator', 'YOURPREFIX-demo-importer', 'YOURPREFIX_demo_importer_page');
}
//admin page
function YOURPREFIX_demo_importer_page() {
global $YOURPREFIX_demo_importer_error;
echo '<div class="wrap"><h1>'.esc_html__('Demo Content Importer', 'cooltheme').'</h1><div>';
}
With these lines we added a new demo importer menu under Appearance in WP Admin Area. It can be accessed only by an administrator or above. Important: You might want to switch YOURPREFIX strings to your unique prefix in all our code examples. Also change translation functions’ text domain (cooltheme) to your theme’s text domain.
Webhosting permission and capability check
Before we start importing we need to make sure that our script is allowed to read and copy files, create directories and read/write database. Different hosting systems have different limitations when it comes to file handling, so we’ll use the WP Filesystem API for file management. Insert the following code above the function YOURPREFIX_demo_importer_page() in importer.inc.php:
// webhosting permission and capability check
if (empty($_POST['YOURPREFIX_importing']) && $_GET['page'] == 'YOURPREFIX-demo-importer' && current_user_can('administrator')) {
// is allow_url_fopen setting on in php.ini?
if (ini_get('allow_url_fopen') != '1' && ini_get('allow_url_fopen') != 'On') {
$YOURPREFIX_demo_importer_selfcheck[] = esc_html__('The allow_url_fopen setting is turned off in the PHP ini!', 'cooltheme');
} else {
// can we read a file with wp filesystem?
global $wp_filesystem;
if (empty($wp_filesystem)) {
require_once(ABSPATH . '/wp-admin/includes/file.php');
WP_Filesystem();
}
if (!$wp_filesystem->get_contents(get_template_directory_uri().'/importer/data.imp')) {
$YOURPREFIX_demo_importer_selfcheck[] = esc_html__('The script couldn\'t read the data.imp file. Is it there? Does it have the permission to read?', 'cooltheme');
}
}
// can we create directory?
$uploads_dir = $wp_filesystem->abspath() . '/wp-content/uploads';
if (!$wp_filesystem->is_dir($uploads_dir)) {
if (!$wp_filesystem->mkdir($uploads_dir)) {
$YOURPREFIX_demo_importer_selfcheck[] = esc_html__('The script couldn\'t create a directory!', 'cooltheme');
}
}
// can we copy files?
if (!$wp_filesystem->copy(get_template_directory().'/importer/media/book.jpg', $wp_filesystem->abspath() . '/wp-content/uploads/test.jpg')) {
$YOURPREFIX_demo_importer_selfcheck[] = esc_html__('The script couldn\'t copy a file!', 'cooltheme');
} else {
$wp_filesystem->delete($wp_filesystem->abspath() . '/wp-content/uploads/test.jpg');
}
// can we read/write database?
global $wpdb;
if (!$wpdb->query('CREATE TABLE IF NOT EXISTS '.$wpdb->prefix.'testing (id mediumint(9) NOT NULL AUTO_INCREMENT, test varchar(255), UNIQUE KEY id (id))')) {
$YOURPREFIX_demo_importer_selfcheck[] = esc_html__('The script is not allowed to write MySQL database!', 'cooltheme');
} else {
if (!$wpdb->query('TRUNCATE TABLE '.$wpdb->prefix.'testing')) {
$YOURPREFIX_demo_importer_selfcheck[] = esc_html__('The script is not allowed to write MySQL database!', 'cooltheme');
}
}
}
In the example above, change book.jpg to a file that actually exists in the /uploads/ directory. As you can see the script collects error messages in an array. Let’s create a section in admin page where we can display these errors. We’ll also add the magic importer buttton and a notification to warn our users about database clearing. So find and extend YOURPREFIX_demo_importer_page() function the following way:
// admin page
function YOURPREFIX_demo_importer_page() {
global $YOURPREFIX_demo_importer_selfcheck, $YOURPREFIX_demo_importer_success;
echo '<div class="wrap"><h1>'.esc_html__('Demo Content Importer', 'cooltheme').'</h1>';
if (empty($_POST['YOURPREFIX_importing'])) {
// welcome message
echo '<p>' . esc_html__('Here you can import sample content with a single click!', 'cooltheme') . '<br /><br />
'. __('<b>WARNING! The importing process will remove your existing posts, pages and media library!<br />
It\'s recommended to use a fresh, clean wordpress install!</b>', 'cooltheme') . '</p>';
// show button if no error were found in selfcheck
if (empty($YOURPREFIX_demo_importer_selfcheck)) {
echo '<form method="post">
<input type="hidden" name="YOURPREFIX_importing" value="1" />
<input type="submit" name="submit" id="submit" class="button button-primary" value="' . esc_attr__('Import Now!', 'cooltheme') . '" />
</form>';
}
} else {
// user pressed the import button
if (!empty($YOURPREFIX_demo_importer_success)) {
//successful import
echo '<p><b>' . __('Demo content has been successfully imported!', 'cooltheme') . '</p>';
} else {
//something went wrong
echo '<p><b>' . __('ERROR! Something went wrong!', 'cooltheme') . '</p>';
}
}
// error messages from webhosting check
if (!empty($YOURPREFIX_demo_importer_selfcheck)) {
echo '<h2 class="title">'.esc_html__('Whooops!', 'cooltheme').'</h2>
<p><b>'.esc_html__('One or more problems were found that needs to be fixed before the import!', 'cooltheme').'</b></p>
<ul>';
foreach ($YOURPREFIX_demo_importer_selfcheck as $err) {
echo '<li>• '. $err .'</li>';
}
echo '</ul>';
}
echo '</div>';
}
You can visit your importer page now to find out if your actual local webserver has any problem with our script and to check for typos.
Copy media files and import MySQL data
If everything seems good at this point, we can copy our demo images/files, clear database tables, read and process data.imp file line by line and import the SQL data. Paste the following code above the YOURPREFIX_demo_importer_page() function:
// start importing
if (!empty($_POST['YOURPREFIX_importing']) && $_GET['page'] == 'YOURPREFIX-demo-importer' && current_user_can('administrator')) {
// copy all media files
global $wp_filesystem;
if (empty($wp_filesystem)) {
require_once(ABSPATH . '/wp-admin/includes/file.php');
WP_Filesystem();
}
$files = glob(get_template_directory().'/importer/media/*.*');
foreach($files as $file) {
if (!$wp_filesystem->copy($file, $wp_filesystem->abspath() . '/wp-content/uploads/' . basename($file))) {
$YOURPREFIX_demo_importer_error = '1';
}
}
// clear tables
global $wpdb;
$wpdb->query('TRUNCATE TABLE '.$wpdb->prefix.'comments');
$wpdb->query('TRUNCATE TABLE '.$wpdb->prefix.'postmeta');
$wpdb->query('TRUNCATE TABLE '.$wpdb->prefix.'posts');
$wpdb->query('TRUNCATE TABLE '.$wpdb->prefix.'term_relationships');
$wpdb->query('TRUNCATE TABLE '.$wpdb->prefix.'term_taxonomy');
$wpdb->query('TRUNCATE TABLE '.$wpdb->prefix.'terms');
// read SQL dump and process each statement
$data = $wp_filesystem->get_contents(get_template_directory_uri().'/importer/data.imp');
$sql = explode('<cooltheme_sep>', $data);
$current_url = get_site_url();
foreach ($sql as $statement) {
if (!empty($statement)) {
// replace default wp prefix to user's choice if it's not the default one
if (strstr($statement,'wp_comments') && $wpdb->prefix != 'wp_') {
$statement = str_replace('wp_comments',$wpdb->prefix.'comments',$statement);
}
if (strstr($statement,'wp_postmeta')) {
if ($wpdb->prefix != 'wp_') {
$statement = str_replace('wp_postmeta',$wpdb->prefix.'postmeta',$statement);
}
// also replace all our sample paths to the user's actual path
$statement = str_replace('http://localhost/cooltheme',$current_url,$statement);
}
if (strstr($statement,'wp_posts')) {
if ($wpdb->prefix != 'wp_') {
$statement = str_replace('wp_posts',$wpdb->prefix.'posts',$statement);
}
// also replace all our sample paths to the user's actual path
$statement = str_replace('http://localhost/cooltheme',$current_url,$statement);
}
if (strstr($statement,'wp_term_relationships') && $wpdb->prefix != 'wp_') {
$statement = str_replace('wp_term_relationships',$wpdb->prefix.'term_relationships',$statement);
}
if (strstr($statement,'wp_term_taxonomy') && $wpdb->prefix != 'wp_') {
$statement = str_replace('wp_term_taxonomy',$wpdb->prefix.'term_taxonomy',$statement);
}
if (strstr($statement,'wp_terms') && $wpdb->prefix != 'wp_') {
$statement = str_replace('wp_terms',$wpdb->prefix.'terms',$statement);
}
// run the query
if (!$wpdb->query($statement)) {
$YOURPREFIX_demo_importer_error = '1';
}
}
}
// navigation, widgets, other settings
if (empty($YOURPREFIX_demo_importer_error)) {
update_option('option_name','option_value');
update_option('option_name',unserialize('serialized_option_value'));
}
// if everything went well
if (empty($YOURPREFIX_demo_importer_error)) {
$YOURPREFIX_demo_importer_success = '1';
}
}
When a user presses the importer button, the $_POST['YOURPREFIX_importing'] variable receives a value and this is our sign to start importing. Note how we replace both instances of our demo path, http://localhost/cooltheme, with the user’s actual site path. Always check this path when you’re building a demo importer for a new theme.
Navigation, widget, and other settings
The last things we need to import are the settings from wp_options table. In your MySQL manager go to your theme’s database and list all contents of wp_options table sorted by option_name column. This table contains all your settings, like navigation, number of posts per page, active widgets, etc. You need to go through each of them to see which one is important for you and insert them with update_option() function, as you can see it in example code above. Some settings are stored in a serialized array. In those cases we need to put the value in an unserialize() function. Most of the option names are self-explanatory.
After testing you’ll see if you missed something anyway. Here are some important options: page_for_posts, page_on_front, show_on_front, sidebars_widgets, theme_mods_YOURTHEME. Widget informations are stored in the options named widget_*.
Testing and Debugging
We are ready to test our script! Create a fresh WordPress install on your local webserver for testing purposes. Copy your theme there, activate it, visit the demo importer page and hit the import button. If something went wrong, PHP error messages should tell you all the details (if they are turned on). You can also look for errors in your server’s log files. If everything went smoothly then your sample content should appear properly.
That’s it!
Besides a demo importer I also recommend you to provide a documentation for your theme which explains step-by-step how a user can set up things like your live preview. This is useful for customers, who don’t want to delete their existing content. At DivPusher WordPress theme club we use this method in our themes as well.
Download the source files
Here is a complete example of the code provided in this tutorial. The download file also includes a set of example media files and ready-to-go import file.
With the discovery that LEGO minifig heads fit rather well in .40 S&W brass, as well as 10mm brass, the next logical step is to test if LEGO heads can be used as an actual projectile. If you missed it, here is the post about LEGO minifig heads fitting in .40 cal brass. I went […]