Building a Marketpress store that has everything on the frontend (no wp-admin/…)

Recently I set a goal:

Create a network of stores where the individual store owners will not need to access the wordpress dashboard for anything. It has been a bumpy ride and it’s not yet finished but it’s pretty close. I thought of sharing my experience here in case someone else can benefit.

1. Create/edit/manage products, orders etc using the frontend

Thankfully I found an excellent plugin called “Marketpress Frontend” which does most of the heavy-lifting. It’s not free but it’s worth it if you’re trying to accomplish the same thing that I was. I chose not to use the built-in widget for the links, instead I created my own implementation to better suit my custom theme. On the attachments below you can see what this “administration menu” looks like (file admin-menu.png).

How this menu was accomplished:

<?php global $wp_roles;
foreach ( $wp_roles->role_names as $role => $name ) :
if (current_user_can( $role ) && $role == 'administrator') { ?>
<li class="menu-dropdown-storeadmin dropdown" data-dropdown="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-cog icon-white"></i><?php _e('Store Administration', 'basic'); ?> <b class="caret"></b></a>
<ul class="dropdown-menu">
<?php if ( class_exists( 'MarketpressFadminWidget' ) ) { ?>
<?php
$mp_frontend_settings = get_option( 'mp_frontend_settings' );
$mp_frontend_settings = unserialize($mp_frontend_settings);
$adminpage = $mp_frontend_settings['page_id'];
$permalink = get_permalink($adminpage);
?>
<li class="menu-product-new"><a href="<?php echo $permalink; ?>"><?php _e('Create Product', 'basic'); ?></a></li>
<li class="menu-product-list">
<a href="<?php echo $permalink; ?>?ptype=allproduct"><?php _e('Products', 'basic'); ?>
<?php $num_posts = wp_count_posts( 'product' );
$num = number_format_i18n( $num_posts->publish );
$count_output = '<span class="badge badge-success">' . $num . '</span>';
echo $count_output;
?>
</a>
</li>
<li class="menu-store-orders">
<a href="<?php echo $permalink; ?>?ptype=orders"><?php _e('Orders', 'basic'); ?>
<?php $num_posts = wp_count_posts('mp_order'); //get pending order count
$count = $num_posts->order_received + $num_posts->order_paid;
if ( $count > 0 )
$count_output = '<span class="badge badge-important">' . $count . '</span>';
else
$count_output = '';
echo $count_output;
?>
</a>
</li>
<li class="menu-store-settings"><a href="<?php echo $permalink; ?>?ptype=settings"><?php _e('Store Settings', 'basic') ?></a></li>
<?php } ?>
<li class="menu-store-statistics"><a data-toggle="modal" href="#statisticsmodal"><?php _e('Store Statistics', 'basic'); ?></a></li>

<li class="divider"></li>

<li class="menu-store-affiliates"><a href="/affiliate"><?php _e('Affiliates Program', 'basic'); ?></a></li>
<?php if ( class_exists( 'ProSites' ) ) { ?>
<li class="menu-store-upgrade"><?php basic_upgrade_link(); ?></li>
<?php } ?>
</ul>
</li>
<?php } endforeach; ?>

I’ll explain it a bit:

<?php global $wp_roles;
foreach ( $wp_roles->role_names as $role => $name ) :
if (current_user_can( $role ) && $role == 'administrator') { ?>
<li class="menu-dropdown-storeadmin dropdown" data-dropdown="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-cog icon-white"></i><?php _e('Store Administration', 'basic'); ?> <b class="caret"></b></a>
<ul class="dropdown-menu">

These lines determine if the user is admin. If he is, then the “Store Administration” menu is displayed. The extra classes and ul’s are for my theming… I’m using bootstrap (via the roots theme) and it has made my theming a lot easier. However some extra classes must be applied in various elements.

<?php if ( class_exists( 'MarketpressFadminWidget' ) ) { ?>
<?php
$mp_frontend_settings = get_option( 'mp_frontend_settings' );
$mp_frontend_settings = unserialize($mp_frontend_settings);
$adminpage = $mp_frontend_settings['page_id'];
$permalink = get_permalink($adminpage);
?>

These lines determine if Marketpress Frontend is installed on our system and retrieve the url of the page we are using for our administration purposes. This url will be used below ($permalink)

<li class="menu-product-new"><a href="<?php echo $permalink; ?>"><?php _e('Create Product', 'basic'); ?></a></li>

The above line is pretty simple… it just creates a link to our product creation page.

<li class="menu-product-list">
<a href="<?php echo $permalink; ?>?ptype=allproduct"><?php _e('Products', 'basic'); ?>
<?php $num_posts = wp_count_posts( 'product' );
$num = number_format_i18n( $num_posts->publish );
$count_output = '<span class="badge badge-success">' . $num . '</span>';
echo $count_output;
?>
</a>
</li>

Now this is nice….

It creates a link to a page where we can see and manage all our products, and right next to it the number of products we have is displayed.

<li class="menu-store-orders">
<a href="<?php echo $permalink; ?>?ptype=orders"><?php _e('Orders', 'basic'); ?>
<?php $num_posts = wp_count_posts('mp_order'); //get pending order count
$count = $num_posts->order_received + $num_posts->order_paid;
if ( $count > 0 )
$count_output = '<span class="badge badge-important">' . $count . '</span>';
else
$count_output = '';
echo $count_output;
?>
</a>
</li>

What these lines do is render the link to our orders management page. The extra lines are to display the number of pending orders for our store (you can get an idea by looking at this menu screenshot I have attached).

<li class="menu-store-settings"><a href="<?php echo $permalink; ?>?ptype=settings"><?php _e('Store Settings', 'basic') ?></a></li>
<?php } ?>
<li class="menu-store-statistics"><a data-toggle="modal" href="#statisticsmodal"><?php _e('Store Statistics', 'basic'); ?></a></li>

These line display these two links: Store settings and Store statistics.

Notice the on the statistics link? We’ll talk about this in a bit. :wink:

<li class="menu-store-affiliates"><a href="/affiliate"><?php _e('Affiliates Program', 'basic'); ?></a></li>

This is a link for the affiliates plugin. The /affiliates path is just a page that contains the below shortcodes:

[affiliatelogincheck]
[affiliateuserdetails]
[affiliatestatschart]
[affiliatestatstable]
[affiliatevisitschart]
[affiliatetopvisitstable]
[affiliatetopvisitstable]

I don’t know why but I couldn’t find them anywhere so I had to dig in the Affiliate Plugin’s code to find them. Maybe they should be made a bit more “visible” in the plugin so that others don’t have to spend their time digging and searching for stuff that’s simple.

<?php if ( class_exists( 'ProSites' ) ) { ?>
<li class="menu-store-upgrade"><?php basic_upgrade_link(); ?></li>
<?php } ?>

What this does is display a button to the pro-sites page. Again I couldn’t find any obvious way to do this, so I edited my functions.php file and added the basic_upgrade_link function. for your convenience, here it is:

function basic_upgrade_link() {
global $blog_id;
if ( current_user_can('edit_pages') ) {
$content = '';
$content .= '<div class="upgrade"><a class="btn" href="' . network_home_url('pro-site/?bid=');
$content .= $blog_id;
$content .= '">' . __('Upgrade your store', 'basic') . '</a></div>';
} else {
$content = '';
}
echo $content;
}

Next: the statistics.

As hinted by the store statistics link above, it opens up in a modal. You can see it on the 2nd screenshot (file statistics.png).

<?php global $wp_roles;
foreach ( $wp_roles->role_names as $role => $name ) :
if (current_user_can( $role ) && $role == 'administrator') { ?>
<div class="modal hide fade in" id="statisticsmodal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3><?php _e('Store Statistics', 'basic'); ?></h3>
</div>
<div class="modal-body">
<?php global $wpdb, $mp;
$year = date('Y');
$month = date('m');
$this_month = $wpdb->get_row("SELECT count(p.ID) as count, sum(m.meta_value) as total, avg(m.meta_value) as average FROM $wpdb->posts p JOIN $wpdb->postmeta m ON p.ID = m.post_id WHERE p.post_type = 'mp_order' AND m.meta_key = 'mp_order_total' AND YEAR(p.post_date) = $year AND MONTH(p.post_date) = $month");

$year = date('Y', strtotime('-1 month'));
$month = date('m', strtotime('-1 month'));
$last_month = $wpdb->get_row("SELECT count(p.ID) as count, sum(m.meta_value) as total, avg(m.meta_value) as average FROM $wpdb->posts p JOIN $wpdb->postmeta m ON p.ID = m.post_id WHERE p.post_type = 'mp_order' AND m.meta_key = 'mp_order_total' AND YEAR(p.post_date) = $year AND MONTH(p.post_date) = $month");

$all_months = $wpdb->get_row("SELECT count(p.ID) as count, sum(m.meta_value) as total, avg(m.meta_value) as average FROM $wpdb->posts p JOIN $wpdb->postmeta m ON p.ID = m.post_id WHERE p.post_type = 'mp_order' AND m.meta_key = 'mp_order_total'");

//later get full stats and graph
//$stats = $wpdb->get_results("SELECT DATE_FORMAT(p.post_date, '%Y-%m') as date, count(p.ID) as count, sum(m.meta_value) as total, avg(m.meta_value) as average FROM $wpdb->posts p JOIN $wpdb->postmeta m ON p.ID = m.post_id WHERE p.post_type = 'mp_order' AND m.meta_key = 'mp_order_total' GROUP BY YEAR(p.post_date), MONTH(p.post_date) ORDER BY date DESC");
?>
<table class="table"><tr><td>
<table class="table table-striped table-bordered table-condensed">
<thead><h4><?php printf(__('This month', 'basic'), date_i18n('M, Y')); ?></h4></thead>
<tbody>
<tr>
<td class="<?php echo ($this_month->count >= $last_month->count) ? ' green' : ' red'; ?>"><?php echo number_format_i18n($this_month->count); ?></td>
<td><?php _e('Orders', 'basic'); ?></td>
</tr>
<tr>
<td class="<?php echo ($this_month->total >= $last_month->total) ? ' green' : ' red'; ?>"><?php echo $mp->format_currency(false, $this_month->total); ?></td>
<td><?php _e('Total Orders', 'basic'); ?></td>
</tr>
<tr>
<td class="<?php echo ($this_month->average >= $last_month->average) ? ' green' : ' red'; ?>"><?php echo $mp->format_currency(false, $this_month->average); ?></td>
<td><?php _e('Order Average', 'basic'); ?></td>
</tr>
</tbody>
</table>
</td><td>
<table class="table table-striped table-bordered table-condensed">
<thead><h4><?php printf(__('Last month', 'basic'), date_i18n('M, Y', strtotime('-1 month'))); ?></h4></thead>
<tbody>
<tr>
<td><?php echo intval($last_month->count); ?></td>
<td><?php _e('Orders', 'basic'); ?></td>
</tr>
<tr>
<td><?php echo $mp->format_currency(false, $last_month->total); ?></td>
<td><?php _e('Total Orders', 'basic'); ?></td>
</tr>
<tr>
<td><?php echo $mp->format_currency(false, $last_month->average); ?></td>
<td><?php _e('Order Average', 'basic'); ?></td>
</tr>
</tbody>
</table>
</td></tr></table>
<table class="table table-striped table-bordered table-condensed">
<thead><tr><th><?php _e('Total', 'basic'); ?></th></tr></thead>
<tr>
<td><?php _e('Total Products', 'basic'); ?></td>
<td>
<?php $num_posts = wp_count_posts( 'product' );
$num = number_format_i18n( $num_posts->publish );
$count_output = '' . $num . '';
echo $count_output;
?>
</td>
</tr>
</tr>
<tr>
<td><?php _e('New Orders', 'basic'); ?></td>
<td class="red"><a class="red" href="/storeadmin/?ptype=orders&post_status=order_received">
<?php $num_posts = wp_count_posts('mp_order');
$count = '' . number_format_i18n($num_posts->order_received) . '';
$count_output = '' . $count . '';
echo $count_output;
?>
</a></td>
</tr>
<tr>
<td><?php _e('Orders with status: Paid', 'basic'); ?></td>
<td class="orange"><a class="oranfe" href="/storeadmin/?ptype=orders&post_status=order_paid">
<?php $num_posts = wp_count_posts('mp_order');
$count = '' . number_format_i18n($num_posts->order_paid) . '';
$count_output = '' . $count . '';
echo $count_output;
?>
</td>
</tr>
<tr>
<td><?php _e('Orders with status: Sent', 'basic'); ?></td>
<td class="green"><a class="green" href="/storeadmin/?ptype=orders&post_status=order_shipped">
<?php $num_posts = wp_count_posts('mp_order');
$count = '' . number_format_i18n($num_posts->order_shipped) . '';
$count_output = '' . $count . '';
echo $count_output;
?>
</td>
</tr>
<tr>
<td><?php _e('Closed Orders', 'basic'); ?></td>
<td class="green"><a class="green" href="/storeadmin/?ptype=orders&post_status=order_closed">
<?php $num_posts = wp_count_posts('mp_order');
$count = '' . number_format_i18n($num_posts->order_closed) . '';
$count_output = '' . $count . '';
echo $count_output;
?>
</td>
</tr>
<tr>
<td><?php _e('Total Number of Orders', 'basic'); ?></td>
<td class="blue"><?php echo intval($all_months->count); ?></td>
</tr>
<tr>
<td><?php _e('Orders Total', 'basic'); ?></td>
<td class="blue"><?php echo $mp->format_currency(false, $all_months->total); ?></td>
</tr>
<tr>
<td><?php _e('Average per Order', 'basic'); ?></td>
<td class="blue"><?php echo $mp->format_currency(false, $all_months->average); ?></td>
</tr>
</table>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" data-dismiss="modal"><?php _e('Close', 'basic'); ?></a>
</div>
</div>
<?php } endforeach; ?>

The logic and most of the code were “hijacked” from the marketpress-stats file in MarketPress core. I’m not goint to explain this, if you look at it it’s pretty self-explanatory. A query is run at the beginning, then the results are displayed in a table . The table resides in a modal window and in charge of rendering this modal is the bootstrap javascript.

What’s next… Oh yes! My network has many many many categories (850+) so I had to display them in a hierarchical way that was easy and intuitive when creating products, so I found the Intuitive Category Checklist plugin that really made it easy. Fot the featured image uploading, I installed the Drag & Drop for Post Thumbnails. With a bit of css styling it was nice… you can see the result of these two in the 3rd screenshot, file named product-creation-sidebar.png

Next, I wanted my users to be able to edit the text for some parts of their sites. So I created a few widgets and installed the Front-End Editor plugin. For example, for the frontpage of their sites I created a widget that allows them to add title, text + buttons though I’m having a slight problem with this one. If you feel like helping out, check out this thread. I’ve got screenshots in there and the actual code can be found here: https://gist.github.com/3004860

Some labels are in Greek (I was too bored at the moment to do _e(‘…’, ‘…’:wink:; and then translate these strings, so I hardcoded them. If anyone need this do be shy, just say so and I’ll do it properly.

What’s next? Well, I can’t think of anything right now so if anything comes to mind I’ll continua at the comments section.