I'm writing a plugin for WordPress and I need to emulate the functionality (and style) of a WP table for the admin area. I mean those basic tables that appear on any menu page, like the ones that list the Pages or the Comments.
I've made some progress on that: I've managed to replicate the WP style by simply using the same CSS classes and HTML that they use in their own tables, but I'm not sure this is the right way to approach this.
I actually only need the pagination and filter functionalities, nothing else. I guess I can put my own logic here and get it done, but I was wondering if there was a simpler, more appropriate way to do this.
So, any thoughts on how should I build a table in the admin area of WordPress so that it matches the style/functionality of the default ones?
(BTW, I'm not using a Custom Post Type, just a custom top-level admin menu. Hence the need for a table made from scratch.)
Here's the code I'm using to display my custom admin page (there are some literal values for simplicity):
HTML:
<div class="wrap">
<h1><?= $helper->esc_html__(get_admin_page_title()) ?></h1>
<hr class="wp-header-end">
<br>
<form>
<div class="tablenav top">
<div class="alignleft actions">
<label for="filter-by-date" class="screen-reader-text">Filter by date</label>
<select name="filter-by-date">
<option value="all" selected>All dates</option>
<option value="last-month">Last month</option>
<option value="last-week">Last week</option>
<option value="today">Today</option>
</select>
<input type="submit" name="filter-action" class="button" value="Filter">
</div>
<div class="tablenav-pages">
<?php
// translators: %d: Total number of elements
$elements_count = sprintf($helper->esc_html__('%d elements'), $requests_count);
?>
<span class='displaying-num'><?= $elements_count ?></span>
<span class="pagination-links">
<span class="tablenav-pages-navspan" aria-hidden="true">«</span>
<a class="prev-page" href="#">
<span class="screen-reader-text">Previous page</span>
<span aria-hidden="true">‹</span>
</a>
<span class="paging-input">
<label for="current-page-selector" class="screen-reader-text">Current page</label>
<input type="text" name="paged" id="current-page-selector" class="current-page" value="2" size="1" aria-describedby="table-paging">
<span class="tablenav-paging-text"> of <span class="total-pages">3</span></span>
</span>
<a class="next-page" href="#">
<span class="screen-reader-text">Next page</span>
<span aria-hidden="true">›</span>
</a>
<span class="tablenav-pages-navspan" aria-hidden="true">»</span>
</span>
</div>
</div>
</form>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th class="manage-column column-primary"><?= $helper->esc_html__('Movie Title') ?></th>
<th class="manage-column"><?= $helper->esc_html__('Release') ?></th>
<th class="manage-column"><?= $helper->esc_html__('Language') ?></th>
<th class="manage-column"><?= $helper->esc_html__('User') ?></th>
<th class="manage-column"><?= $helper->esc_html__('Date') ?></th>
<th class="manage-column"><?= $helper->esc_html__('Actions') ?></th>
</tr>
</thead>
<tbody id="the-list">
<!-- If there are any requests, display each record, otherwise, inform the user -->
<?php if ($requests_count): ?>
<?php foreach ($requests as $request): ?>
<tr class="is-expanded">
<td class="column-primary" data-colname="Movie Title">
<strong><?= $helper->esc_html__($request->title) ?></strong>
</td>
<td data-colname="Release">
<?php if ($request->release === null): ?>
<span class="sr-text-info"><?= $helper->esc_html__('Not Specified') ?></span>
<?php else: ?>
<?= esc_html($request->release); ?>
<?php endif; ?>
</td>
<td data-colname="Language"><?= $helper->esc_html__($request->language) ?></td>
<td data-colname="User">
<?php if ($request->user === null): ?>
<span class="sr-text-info"><?= $helper->esc_html__('Anonimous') ?></span>
<?php else: ?>
<?= esc_html($request->user); ?>
<?php endif; ?>
</td>
<td data-colname="Date">
<?php
$date = new DateTime($request->date);
// translators: draft request date format, see http://php.net/date
$request_date_fmt = $helper->__('m/d/Y, g:i a');
echo esc_html($date->format($request_date_fmt));
?>
</td>
<td data-colname="Actions">
<a class="button" href="#">Delete</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr class="is-expanded">
<td class="sr-text-center" colspan="6">
<span class="sr-text-info"><?= $helper->esc_html__('There are no requests to show.') ?></span>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
Thanks in advance!
The best option is to extend the native WP_List_Table class which is already available in WordPress Core. While this is a private class in core, it probably won't change drastically in the future and I would personally consider it safe to extend.
When you define your own class and extend WP_List_Table, all you'll need to do is create your own methods with the logic for the data. The best part is that the parent WP_List_Table class will handle everything else for you (markup, styles, pagination, etc).
WP Engineer has a good beginner's guide to get you started. You can also search the web for "WP_List_Table guide", "WP_List_Table tutorial", (etc) and you'll find plenty of examples online.
Related
I'm trying to create a dynamic content field where each data is in a separate accordion and am having a bit of trouble.
Result is the first accordion only works even i am having different id's for each accordion.
Can anyone help me on this
while ($row = mysqli_fetch_assoc($sql_result))
{
$i=1;
?>
<div class="container-fluid border">
<table width=100%>
<tr>
<td><img src="/images/vehicles/<?php echo strtolower($row['vtype']); ?>.jpg" width="75" heigth="75" alt=""></td>
<td align="center"><b><?php echo $row['vtype']; ?> A/C </b> - <?php echo $row['nos']; ?> Seater<br>
<b>₹ <?php if($duration=='8hrs') { $basefare=$row['8hrs']; } elseif($duration=='10hrs') { $basefare=$row['10hrs']; } else { $basefare=$row['12hrs']; } $base=$basefare+($basefare*5/100); echo number_format($base,2,".",","); ?> </b></td>
<td align="right">Book</td>
</tr>
</table>
</div>
<div id="accordionfare<?php echo $i; ?>">
<div class="card">
<div class="card-header" align="right">
<a class="card-link" data-toggle="collapse" href="#collapsefare<?php echo $i; ?>">Fare Details</a>
</div>
<div id="collapsefare<?php echo $i; ?>" class="collapse" data-parent="#accordionfare<?php echo $i; ?>">
<div class="card-body">
<p align="center"><b>Fare Details: </b>Base Fare: ₹ <?php echo number_format($basefare,2,".",","); ?> | GST # 5%: ₹ <?php echo number_format(round($base*5/100),2,".",","); ?> | Extra Hr Fare: ₹ <?php echo number_format($row['extrahr'],2,".",","); ?>/Hr | Extra Km Fare: ₹ <?php echo number_format($row['extrakm'],2,".",","); ?>/Km</p>
<?php $i++; ?>
</div>
</div>
</div>
</div><br>
<?php
}
?>
I've tested your code replacing your data and it works as expected. Check you PHP error log in case there are errors which might brake the loop in some way.
Here is a sandbox where you can Execute my test code and check the HTML result
P.S. You must have accordions with different ids in order to open each one separately.
I'm attempting to show users the current index of the slide they are on. Using WP Alchemy, I can get all the labels and inputs to display their names and values correctly. However, when trying to use the_index(), I can't get the correct index to echo after adding more than 2 slides.
I've removed all my javascript to ensure it wasn't something that was conflicting with jQuery's .sortable(). After the second slide, all names and indexes outside a label or input is: _homepage_slider[slides_2][1][handle] or 1. Anything I need to do, or will it only work in inputs and labels?
<?php global $wpalchemy_media_access; ?>
<div class="my_meta_control">
<?php while($mb->have_fields_and_multi('slides_2')): ?>
<?php $mb->the_group_open(); ?>
<table class="meta-field sortable table" cellspacing="0">
<tr>
<td width="30" class="handle"><?php $mb->the_index() //Only echoes '1' after the second iteration ?></td>
<td width="280" class="imagefield">
<?php $mb->the_field('imgurl'); ?>
<?php $wpalchemy_media_access->setGroupName('img-n'. $mb->get_the_index())->setInsertButtonLabel('Insert'); ?>
<img data-image="mediafield-img-n<?php $mb->the_index() ?>" src="<?php bloginfo('template_url') ?>/framework/images/cm-no-image.jpg" />
<div class="media-field-input hide">
<?php echo $wpalchemy_media_access->getField(array('name' => $mb->get_the_name(), 'value' => $mb->get_the_value())); ?>
</div>
<div class="add-media-btn">
<?php echo $wpalchemy_media_access->getButton(); ?>
</div>
</td>
<td class="last-col">
<?php $mb->the_field('title'); ?>
<label for="<?php $mb->the_name(); ?>">Title</label>
<div><input type="text" id="<?php $mb->the_name(); ?>" name="<?php $mb->the_name(); ?>" value="<?php $mb->the_value(); ?>"/></div>
<?php $mb->the_field('handle'); ?>
<p>Name: <?php $mb->the_name(); //Only echoes _homepage_slider[slides_2][1][handle] after second iteration ?></p>
<p>Index: <?php $mb->the_index(); //Only echoes '1' after second iteration ?></p>
Remove Slide
</td>
</tr>
</table><!-- /.meta-field -->
<?php $mb->the_group_close(); ?>
<?php endwhile; ?>
<p style="margin-bottom:15px; padding-top:5px;">Add</p>
</div>
For any of you wondering, WP Alchemy doesn't work this way. You can add field identifiers to the name, class, id, or value attributes of an element. When echoing the index, it needs a "n" in front of it.
For example:
<?php $mb->the_field('handle'); ?>
<input type="hidden" class="anything-i-want-n<?php $mb->the_index() //This will work ?>" name="<?php $name = $mb->get_the_name() ?>" />
<?php echo $mb->the_index() //This will not ?>
I've been trying to pull one single customer review onto the product page.
I've no code to show as honestly i'm not sure where to start and can't find any mention of it online.
Anyone have any ideas?
I wrote a tutorial earlier on bringing all the review elements onto the product page, so you could follow this tutorial: http://www.e-commercewebdesign.co.uk/blog/magento-tutorials/product-reviews-on-product-view-page.php
All you'd have to do is rename the list block and bring it out in the same way. Then simply modify the loop which brings out the reviews in any way you see fit. E.g. limit to a certain number or only echo out review from a certain user.
EDIT:
To get the latest review is quite simple because the reviews are in date order anyway.
Go to review > product > list.phtml
Replace the code in that file with this:
<?php $_items = $this->getReviewsCollection()->getItems();?>
<div class="box-collateral box-reviews" id="customer-reviews">
<?php if (count($_items)):?>
<h2><?php echo $this->__('Customer Reviews') ?></h2>
<?php echo $this->getChildHtml('toolbar') ?>
<dl>
<?php $r_count = 0; ?>
<?php foreach ($_items as $_review):?>
<?php if ($r_count == 0) { ?>
<dt>
<?php echo $this->htmlEscape($_review->getTitle()) ?> <?php echo $this->__('Review by <span>%s</span>', $this->htmlEscape($_review->getNickname())) ?>
</dt>
<dd>
<?php $_votes = $_review->getRatingVotes(); ?>
<?php if (count($_votes)): ?>
<table class="ratings-table">
<col width="1" />
<col />
<tbody>
<?php foreach ($_votes as $_vote): ?>
<tr>
<th><?php echo $this->escapeHtml($_vote->getRatingCode()) ?></th>
<td>
<div class="rating-box">
<div class="rating" style="width:<?php echo $_vote->getPercent() ?>%;"></div>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php echo nl2br($this->htmlEscape($_review->getDetail())) ?>
<small class="date"><?php echo $this->__('(Posted on %s)', $this->formatDate($_review->getCreatedAt()), 'long') ?></small>
</dd>
<?php } ?>
<?php $r_count++; ?>
<?php endforeach; ?>
</dl>
<?php echo $this->getChildHtml('toolbar') ?>
<?php endif;?>
<?php echo $this->getChildHtml('review_form') ?>
</div>
I've simple put an interator into the loop $r_count and put a check inside the foreach which prevent it from progressing on the next loop iteration.
I can not figure out how to make one row of my table display on another page. What I want to do make each individual row printable. Here is a screen shot.
Here is the php used to display the table (coupon_index.php):
<?php include template("header");?>
<div id="bdw" class="bdw">
<div id="bd" class="cf">
<div id="coupons">
<div class="dashboard" id="dashboard">
<ul><?php echo current_account('/coupon/index.php'); ?></ul>
</div>
<div id="content" class="coupons-box clear">
<div class="box clear">
<div class="box-top"></div>
<div class="box-content">
<div class="head">
<h2>My <?php echo $INI['system']['couponname']; ?></h2>
<ul class="filter">
<li class="label">Category: </li>
<?php echo current_coupon_sub('index'); ?>
</ul>
</div>
<div class="sect">
<?php if($selector=='index'&&!$coupons){?>
<div class="notice">There is no usable <?php echo $INI['system']['couponname']; ?></div>
<?php }?>
<table id="orders-list" cellspacing="0" cellpadding="0" border="0" class="coupons-table">
<tr><th width="300">Deal Item</th><th width="100" nowrap>Voucher's Number</th><th width="60" nowrap>Voucher's Password</th><th width="100" nowrap>Valid Till</th><th width="40">Email Voucher</th></tr>
<?php if(is_array($coupons)){foreach($coupons AS $index=>$one) { ?>
<tr <?php echo $index%2?'':'class="alt"'; ?>>
<td><a class="deal-title" href="/team.php?id=<?php echo $one['team_id']; ?>" target="_blank"><?php echo $teams[$one['team_id']]['title']; ?></a></td>
<td><?php echo $two['id']; ?></td>
<td><?php echo $two['secret']; ?></td>
<td><?php echo date('Y-m-d', $two['expire_time']); ?></td>
<td>Send</td>
</tr>
<?php }}?>
<tr><td colspan="5"><?php echo $pagestring; ?></td></tr>
</table>
</div>
</div>
<div class="box-bottom"></div>
</div>
</div>
<div id="sidebar">
</div>
</div>
</div> <!-- bd end -->
</div> <!-- bdw end -->
<?php include template("footer");?>
and more (index.php):
<?php
require_once(dirname(dirname(__FILE__)) . '/app.php');
need_login();
$daytime = strtotime(date('Y-m-d'));
$condition = array(
'user_id' => $login_user_id,
'consume' => 'N',
"expire_time >= {$daytime}",
);
$count = Table::Count('coupon', $condition);
list($pagesize, $offset, $pagestring) = pagestring($count, 10);
$coupons = DB::LimitQuery('coupon', array(
'condition' => $condition,
'coupon' => 'ORDER BY create_time DESC',
'size' => $pagesize,
'offset' => $offset,
));
$team_ids = Utility::GetColumn($coupons, 'team_id');
$teams = Table::Fetch('team', $team_ids);
include template('coupon_index');
Here is the html(coupon_index.html):
<!--{include header}-->
<div id="bdw" class="bdw">
<div id="bd" class="cf">
<div id="coupons">
<div class="dashboard" id="dashboard">
<ul>${current_account('/coupon/index.php')}</ul>
</div>
<div id="content" class="coupons-box clear">
<div class="box clear">
<div class="box-top"></div>
<div class="box-content">
<div class="head">
<h2>My {$INI['system']['couponname']}</h2>
<ul class="filter">
<li class="label">Category: </li>
${current_coupon_sub('index')}
</ul>
</div>
<div class="sect">
<!--{if $selector=='index'&&!$coupons}-->
<div class="notice">There is no usable {$INI['system']['couponname']}</div>
<!--{/if}-->
<table id="orders-list" cellspacing="0" cellpadding="0" border="0" class="coupons-table">
<tr><th width="300">Deal Item</th><th width="100" nowrap>Voucher's Number</th><th width="60" nowrap>Voucher's Password</th><th width="100" nowrap>Valid Till</th><th width="40">Print Voucher</th></tr>
<!--{loop $coupons $index $one}-->
<tr ${$index%2?'':'class="alt"'}>
<td><a class="deal-title" href="/team.php?id={$one['team_id']}" target="_blank">{$teams[$one['team_id']]['title']}</a></td>
<td>{$one['id']}</td>
<td>{$one['secret']}</td>
<td>${date('Y-m-d', $one['expire_time'])}</td>
<td></td>
</tr>
<!--{/loop}-->
<tr><td colspan="5">{$pagestring}</td></tr>
</table>
</div>
</div>
<div class="box-bottom"></div>
</div>
</div>
<div id="sidebar">
</div>
</div>
</div> <!-- bd end -->
</div> <!-- bdw end -->
<!--{include footer}-->
The simplest solution to output a row per page when printing is simply to use page-break-before or page-break-after CSS properties. That said, you'll need to supress these on either the first or last tr depending on which route you choose.
e.g.:
<style>
#media print {
table tr {page-break-after:always}
}
</style>
you need to pass the database rowID of the coupon in question, or some variation thereof. I would use some hash of it, maybe the MD5 of it, along with a check on the far side that the session person is the owner of that coupon and the coupon is valid...
so your print link would be:
<td>Send</td>
in coupon_index.php i cant understand what $two refers to.
you probably need to better name and indent that piece of code, find a suitable id for the row you want to display, passing this information to the appropriate controller which will load only the relevant row.
Thanks for looking on this problem.
I have a page that is totally valid page, and there is a PHP loop that brings in a <li> for each entry of the table.
When i check this page locally it looks 100% OK, but when veiwing the page online the left side bar (which creates this markup is broken randomly mixing <div>'s and <li>'s and i have no clue what the problem is.
This problem is on FF mac and PC (safari looks good)
See page (problem is on the left side)
php code
<?php do { ?>
<li class="clear-block" id="<?php echo $row_Recordset1['penSKU']; ?>">
<a title="Click to view the <?php echo $row_Recordset1['penName']; ?> collection" rel="<?php echo $row_Recordset1['penSKU']; ?>">
<img src="prodImages/small/<?php echo $row_Recordset1['penSKU']; ?>.png" alt="" />
<div class="prodInfoCntnr">
<div class="basicInfo">
<span class="prodName"><?php echo $row_Recordset1['penName']; ?></span>
<span class="prodSku"><?php echo $row_Recordset1['penSKU']; ?></span>
</div>
<div class="secondaryInfo">
<span>As low as .<?php echo $row_Recordset1['price25000']; ?>¢ <!--<em>(R)</em>--></span>
<div class="colorPlacholder" rel="<?php echo $row_Recordset1['penColors']; ?>"></div>
</div>
</div>
<div class="additPenInfo">
<div class="imprintInfo"><span>Imprint area: </span><?php echo $row_Recordset1['imprintArea']; ?></div>
<div class="colorInfo"><span>Available in: </span><?php echo $row_Recordset1['penColors']; ?></div>
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<th>Amount</th>
<th>500</th>
<th>1,000</th>
<th>2,500</th>
<th>5,000</th>
<th>10,000</th>
<th>20,000</th>
</tr>
<tr>
<td>Price <span>(R)</span></td>
<td><?php echo $row_Recordset1['price500'];?>¢</td>
<td><?php echo $row_Recordset1['price1000'];?>¢</td>
<td><?php echo $row_Recordset1['price2500'];?>¢</td>
<td><?php echo $row_Recordset1['price5000'];?>¢</td>
<td>Please Contact</td>
<td>Please Contact</td>
</tr>
</table>
</div>
</a>
</li>
<?php } while ($row_Recordset1 = mysql_fetch_assoc($Recordset1)); ?>
You cannot put a <div> inside of <a>.
Divs are block level elements. Anchors are not. Basically, it's like putting <span> outside of <div>. Doesn't make any sense.
Solution: Move the anchors to inside the divs.
(In the future, if different browsers are displaying it differently, it's probably not the PHP but the HTML.)