#337947 by codycraven, cwgordon7, yoroy, et al: Add a 'recent content block' for use on the dashboard.
parent
9667f4726d
commit
3e552053f1
|
@ -147,3 +147,14 @@
|
|||
width: 30px;
|
||||
height: 1.6em;
|
||||
}
|
||||
|
||||
/* Recent content block */
|
||||
#dashboard #block-node-recent table,
|
||||
#dashboard #block-node-recent tr {
|
||||
border: none;
|
||||
}
|
||||
|
||||
#dashboard .list-all {
|
||||
text-align: right;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
|
|
@ -166,6 +166,12 @@ function node_theme() {
|
|||
'node_admin_overview' => array(
|
||||
'variables' => array('name' => NULL, 'type' => NULL),
|
||||
),
|
||||
'node_recent_block' => array(
|
||||
'variables' => array('nodes' => NULL),
|
||||
),
|
||||
'node_recent_content' => array(
|
||||
'variables' => array('node' => NULL),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2007,6 +2013,9 @@ function node_block_info() {
|
|||
$blocks['syndicate']['info'] = t('Syndicate');
|
||||
// Not worth caching.
|
||||
$blocks['syndicate']['cache'] = DRUPAL_NO_CACHE;
|
||||
|
||||
$blocks['recent']['info'] = t('Recent content');
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
|
@ -2014,12 +2023,144 @@ function node_block_info() {
|
|||
* Implements hook_block_view().
|
||||
*/
|
||||
function node_block_view($delta = '') {
|
||||
$block['subject'] = t('Syndicate');
|
||||
$block['content'] = theme('feed_icon', array('url' => url('rss.xml'), 'title' => t('Syndicate')));
|
||||
$block = array();
|
||||
|
||||
switch ($delta) {
|
||||
case 'syndicate':
|
||||
$block['subject'] = t('Syndicate');
|
||||
$block['content'] = theme('feed_icon', array('url' => url('rss.xml'), 'title' => t('Syndicate')));
|
||||
break;
|
||||
|
||||
case 'recent':
|
||||
if (user_access('access content')) {
|
||||
$block['subject'] = t('Recent content');
|
||||
if ($nodes = node_get_recent(variable_get('node_recent_block_count', 10))) {
|
||||
$block['content'] = theme('node_recent_block', array(
|
||||
'nodes' => $nodes,
|
||||
));
|
||||
} else {
|
||||
$block['content'] = t('No content available.');
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_configure().
|
||||
*/
|
||||
function node_block_configure($delta = '') {
|
||||
$form = array();
|
||||
if ($delta == 'recent') {
|
||||
$form['node_recent_block_count'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Number of recent content items to display'),
|
||||
'#default_value' => variable_get('node_recent_block_count', 10),
|
||||
'#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 30)),
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_save().
|
||||
*/
|
||||
function node_block_save($delta = '', $edit = array()) {
|
||||
if ($delta == 'recent') {
|
||||
variable_set('node_recent_block_count', $edit['node_recent_block_count']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the most recent nodes that are available to the current user.
|
||||
*
|
||||
* @param $number
|
||||
* (optional) The maximum number of nodes to find. Defaults to 10.
|
||||
*
|
||||
* @return
|
||||
* An array of partial node objects or an empty array if there are no recent
|
||||
* nodes visible to the current user.
|
||||
*/
|
||||
function node_get_recent($number = 10) {
|
||||
$query = db_select('node', 'n');
|
||||
|
||||
if (!user_access('bypass node access')) {
|
||||
// If the user is able to view their own unpublished nodes, allow them
|
||||
// to see these in addition to published nodes. Check that they actually
|
||||
// have some unpublished nodes to view before adding the condition.
|
||||
if (user_access('view own unpublished content') && $own_unpublished = db_query('SELECT nid FROM {node} WHERE uid = :uid AND status = :status', array(':uid' => $GLOBALS['user']->uid, ':status' => NODE_NOT_PUBLISHED))->fetchCol()) {
|
||||
$query->condition(db_or()
|
||||
->condition('n.status', NODE_PUBLISHED)
|
||||
->condition('n.nid', $own_unpublished, 'IN')
|
||||
);
|
||||
}
|
||||
else {
|
||||
// If not, restrict the query to published nodes.
|
||||
$query->condition('n.status', NODE_PUBLISHED);
|
||||
}
|
||||
}
|
||||
$nids = $query
|
||||
->fields('n', array('nid'))
|
||||
->orderBy('changed', 'DESC')
|
||||
->range(0, $number)
|
||||
->addTag('node_access')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
|
||||
$nodes = node_load_multiple($nids);
|
||||
|
||||
return $nodes ? $nodes : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted list of recent nodes.
|
||||
*
|
||||
* @return
|
||||
* The recent content table HTML.
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_node_recent_block($variables) {
|
||||
$rows = array();
|
||||
$output = '';
|
||||
|
||||
$l_options = array('query' => drupal_get_destination());
|
||||
foreach ($variables['nodes'] as $node) {
|
||||
$row = array();
|
||||
$row[] = theme('node_recent_content', array('node' => $node));
|
||||
$row[] = node_access('update', $node) ? l(t('edit'), 'node/' . $node->nid . '/edit', $l_options) : '';
|
||||
$row[] = node_access('delete', $node) ? l(t('delete'), 'node/' . $node->nid . '/delete', $l_options) : '';
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
if ($rows) {
|
||||
$output = theme('table', array('rows' => $rows));
|
||||
$output .= '<div class="list-all">' . l(t('Show all content'), 'admin/content') . '</div>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted recent node to be displayed in the recent content block.
|
||||
*
|
||||
* @return
|
||||
* The recent content node's HTML.
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_node_recent_content($variables) {
|
||||
$node = $variables['node'];
|
||||
|
||||
$output = '<div class="node-title">';
|
||||
$output .= l($node->title, 'node/' . $node->nid);
|
||||
$output .= theme('mark', array('type' => node_mark($node->nid, $node->changed)));
|
||||
$output .= '</div><div class="node-author">';
|
||||
$output .= theme('username', array('account' => user_load($node->uid)));
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic function for generating RSS feeds from a set of nodes.
|
||||
*
|
||||
|
|
|
@ -378,7 +378,7 @@ class NodeCreationTestCase extends DrupalWebTestCase {
|
|||
|
||||
// Check that the failed rollback was logged.
|
||||
$records = db_query("SELECT wid FROM {watchdog} WHERE message LIKE 'Explicit rollback failed%'")->fetchAll();
|
||||
$this->assertTrue(count($records) > 0, t('Transactions not supported, and rollback error logged to watchdog.'));
|
||||
$this->assertTrue(count($records) > 0, t('Transactions not supported, and rollback error logged to watchdog.'));
|
||||
}
|
||||
|
||||
// Check that the rollback error was logged.
|
||||
|
@ -1170,3 +1170,109 @@ class NodeFeedTestCase extends DrupalWebTestCase {
|
|||
$this->assertTrue(strpos($output, '<copyright>Drupal is a registered trademark of Dries Buytaert.</copyright>') !== FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional tests for the node module blocks.
|
||||
*/
|
||||
class NodeBlockFunctionalTest extends DrupalWebTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Node blocks',
|
||||
'description' => 'Test node block functionality.',
|
||||
'group' => 'Node',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('node');
|
||||
|
||||
// Create users and test node.
|
||||
$this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer nodes', 'administer blocks'));
|
||||
$this->web_user = $this->drupalCreateUser(array('access content', 'create article content'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the recent comments block.
|
||||
*/
|
||||
function testRecentNodeBlock() {
|
||||
$this->drupalLogin($this->admin_user);
|
||||
|
||||
// Disallow anonymous users to view content.
|
||||
user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array(
|
||||
'access content' => FALSE,
|
||||
));
|
||||
|
||||
// Set the block to a region to confirm block is available.
|
||||
$edit = array(
|
||||
'node_recent[region]' => 'sidebar_first',
|
||||
);
|
||||
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
|
||||
$this->assertText(t('The block settings have been updated.'), t('Block saved to first sidebar region.'));
|
||||
|
||||
// Set block title and variables.
|
||||
$block = array(
|
||||
'title' => $this->randomName(),
|
||||
'node_recent_block_count' => 2,
|
||||
);
|
||||
$this->drupalPost('admin/structure/block/manage/node/recent/configure', $block, t('Save block'));
|
||||
$this->assertText(t('The block configuration has been saved.'), t('Block saved.'));
|
||||
|
||||
// Test that block is not visible without nodes
|
||||
$this->drupalGet('');
|
||||
$this->assertText(t('No content available.'), t('Block with "No content available." found.'));
|
||||
|
||||
// Add some test nodes.
|
||||
$default_settings = array('uid' => $this->web_user->uid, 'type' => 'article');
|
||||
$node1 = $this->drupalCreateNode($default_settings);
|
||||
$node2 = $this->drupalCreateNode($default_settings);
|
||||
$node3 = $this->drupalCreateNode($default_settings);
|
||||
|
||||
// Change the changed time for node so that we can test ordering.
|
||||
db_update('node')
|
||||
->fields(array(
|
||||
'changed' => $node1->changed + 100,
|
||||
))
|
||||
->condition('nid', $node2->nid)
|
||||
->execute();
|
||||
db_update('node')
|
||||
->fields(array(
|
||||
'changed' => $node1->changed + 200,
|
||||
))
|
||||
->condition('nid', $node3->nid)
|
||||
->execute();
|
||||
|
||||
// Test that a user without the 'access content' permission cannot
|
||||
// see the block.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($block['title'], t('Block was not found.'));
|
||||
|
||||
// Test that only the 2 latest nodes are shown.
|
||||
$this->drupalLogin($this->web_user);
|
||||
$this->assertNoText($node1->title, t('Node not found in block.'));
|
||||
$this->assertText($node2->title, t('Node found in block.'));
|
||||
$this->assertText($node3->title, t('Node found in block.'));
|
||||
|
||||
// Check to make sure nodes are in the right order.
|
||||
$this->assertTrue($this->xpath('//div[@id="block-node-recent"]/div/table/tbody/tr[position() = 1]/td/div/a[text() = "' . $node3->title . '"]'), t('Nodes were ordered correctly in block.'));
|
||||
|
||||
// Set the number of recent nodes to show to 10.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->admin_user);
|
||||
$block = array(
|
||||
'node_recent_block_count' => 10,
|
||||
);
|
||||
$this->drupalPost('admin/structure/block/manage/node/recent/configure', $block, t('Save block'));
|
||||
$this->assertText(t('The block configuration has been saved.'), t('Block saved.'));
|
||||
|
||||
// Post an additional node.
|
||||
$node4 = $this->drupalCreateNode($default_settings);
|
||||
|
||||
// Test that all four nodes are shown.
|
||||
$this->drupalGet('');
|
||||
$this->assertText($node1->title, t('Node found in block.'));
|
||||
$this->assertText($node2->title, t('Node found in block.'));
|
||||
$this->assertText($node3->title, t('Node found in block.'));
|
||||
$this->assertText($node4->title, t('Node found in block.'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,16 @@ function standard_install() {
|
|||
'pages' => '',
|
||||
'cache' => -1,
|
||||
),
|
||||
array(
|
||||
'module' => 'node',
|
||||
'delta' => 'recent',
|
||||
'theme' => 'seven',
|
||||
'status' => 1,
|
||||
'weight' => 10,
|
||||
'region' => 'dashboard_main',
|
||||
'pages' => '',
|
||||
'cache' => -1,
|
||||
),
|
||||
array(
|
||||
'module' => 'user',
|
||||
'delta' => 'login',
|
||||
|
|
|
@ -33,12 +33,7 @@ legend {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
#page h1,
|
||||
#page h2,
|
||||
#page h3,
|
||||
#page h4,
|
||||
#page h5,
|
||||
#page h6 {
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: bold;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
@ -125,16 +120,36 @@ abbr, acronym {
|
|||
border-bottom: dotted 1px;
|
||||
}
|
||||
|
||||
ul li, .item-list ul li {
|
||||
ul, .block ul, .item-list ul, .item-list ul {
|
||||
list-style-type: disc;
|
||||
list-style-image: none;
|
||||
margin: 0.25em 0 0.25em 1.5em;
|
||||
}
|
||||
|
||||
.item-list ul li, li.leaf, ul.menu li {
|
||||
list-style-type: disc;
|
||||
list-style-image: none;
|
||||
}
|
||||
|
||||
ul.menu li {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style-type: decimal;
|
||||
margin: 0.25em 0 0.25em 2em;
|
||||
}
|
||||
|
||||
.item-list ul li.collapsed, ul.menu li.collapsed {
|
||||
list-style-image:url(../../misc/menu-collapsed.png);
|
||||
list-style-type:disc;
|
||||
}
|
||||
|
||||
.item-list ul li.expanded, ul.menu li.expanded {
|
||||
list-style-image:url(../../misc/menu-expanded.png);
|
||||
list-style-type:circle;
|
||||
}
|
||||
|
||||
quote, code {
|
||||
margin: .5em 0;
|
||||
}
|
||||
|
@ -292,6 +307,7 @@ div.status {
|
|||
|
||||
#branding h1.page-title {
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding-bottom: 10px;
|
||||
font-size: 18px;
|
||||
font-weight: normal;
|
||||
|
@ -396,31 +412,18 @@ ul.secondary li.active a.active {
|
|||
* Page layout.
|
||||
*/
|
||||
#page {
|
||||
padding-bottom: 40px;
|
||||
padding: 20px 0 40px 0;
|
||||
margin-right: 40px;
|
||||
margin-left: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#page {
|
||||
padding: 20px 0;
|
||||
background: #fff;
|
||||
position: relative;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#page ul.menu li,
|
||||
#page ul.menu li a,
|
||||
#secondary-links ul.links li,
|
||||
#secondary-links ul.links li a {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#page ul.menu li,
|
||||
#secondary-links ul.links li {
|
||||
padding: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
#page ul.menu li a,
|
||||
#secondary-links ul.links li a {
|
||||
font-size: 9px;
|
||||
line-height: 10px;
|
||||
|
@ -439,8 +442,8 @@ ul.secondary li.active a.active {
|
|||
background: #999;
|
||||
}
|
||||
|
||||
#page ul.links li,
|
||||
#page ul.inline li {
|
||||
ul.links li,
|
||||
ul.inline li {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
|
@ -448,22 +451,13 @@ ul.inline li {
|
|||
display: inline;
|
||||
}
|
||||
|
||||
#page ul.menu li a {
|
||||
background: #f8f8f8;
|
||||
color: #05a;
|
||||
}
|
||||
|
||||
#page ul.menu li a:hover {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
#secondary-links ul.links li.active-trail a,
|
||||
#secondary-links ul.links li a.active {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
#page ul.node-type-list li,
|
||||
#page ul.admin-list li {
|
||||
ul.node-type-list li,
|
||||
ul.admin-list li {
|
||||
position: relative;
|
||||
padding-left: 30px;
|
||||
padding-top: 9px;
|
||||
|
@ -475,32 +469,46 @@ ul.inline li {
|
|||
list-style-image: none;
|
||||
}
|
||||
|
||||
#page ul.admin-list.compact {
|
||||
.admin-panel .item-list ul,
|
||||
ul.admin-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.admin-panel .item-list ul,
|
||||
ul.admin-list.compact {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#page ul.admin-list.compact li {
|
||||
border: 0;
|
||||
.admin-panel .item-list li,
|
||||
ul.admin-list.compact li {
|
||||
border: none;
|
||||
background: none;
|
||||
margin-bottom: 2px;
|
||||
padding-top: 2px;
|
||||
margin: 0.25em 0 0.25em 1.5em;
|
||||
padding: 0;
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
#page ul.admin-list li:last-child {
|
||||
ul.admin-list li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#page ul.node-type-list .label {
|
||||
ul.node-type-list .label {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#page ul.node-type-list li a, #page ul.admin-list li a {
|
||||
ul.node-type-list li a, ul.admin-list li a {
|
||||
margin-left: -30px;
|
||||
padding: 0px 0 4px 30px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#page ul.node-type-list li div.description a, #page ul.admin-list li div.description a {
|
||||
ul.admin-list.compact li a {
|
||||
margin-left: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.node-type-list li div.description a, ul.admin-list li div.description a {
|
||||
margin-left: 0px;
|
||||
padding: 0px;
|
||||
min-height: inherit;
|
||||
|
@ -708,6 +716,10 @@ div.form-item div.description {
|
|||
color: #666;
|
||||
}
|
||||
|
||||
ul.tips li {
|
||||
margin: 0.25em 0 0.25em 1.5em;
|
||||
}
|
||||
|
||||
body div.form-type-radio div.description, body div.form-type-checkbox div.description {
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
@ -716,6 +728,7 @@ body div.form-type-radio div.description, body div.form-type-checkbox div.descri
|
|||
input.form-submit, a.button {
|
||||
cursor: pointer;
|
||||
padding: 4px 17px;
|
||||
margin-bottom: 1em;
|
||||
color: #5a5a5a;
|
||||
text-align: center;
|
||||
font-weight: normal;
|
||||
|
@ -797,10 +810,6 @@ ul.action-links {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
#page ul.action-links {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.action-links li {
|
||||
float: left;
|
||||
margin: 0 1em 0 0;
|
||||
|
@ -814,7 +823,7 @@ ul.action-links a {
|
|||
|
||||
/* Exceptions */
|
||||
#diff-inline-form select,
|
||||
#page div.filter-options select {
|
||||
div.filter-options select {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
@ -840,7 +849,7 @@ div.admin-panel {
|
|||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
#page div.admin-panel h3 {
|
||||
div.admin-panel h3 {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
|
@ -852,28 +861,29 @@ div.admin-panel {
|
|||
}
|
||||
|
||||
/* admin/appearance */
|
||||
#page #system-themes-page h2 {
|
||||
#system-themes-page h2 {
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
#page .theme-selector h3 {
|
||||
.theme-selector h3 {
|
||||
font-weight: normal;
|
||||
}
|
||||
#page .theme-default h3 {
|
||||
.theme-default h3 {
|
||||
font-weight: bold;
|
||||
}
|
||||
#page .system-themes-list-enabled .theme-selector h3 {
|
||||
.system-themes-list-enabled .theme-selector h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* admin/content and admin/people */
|
||||
#page dl.multiselect,
|
||||
#page dl.multiselect dt,
|
||||
#page dl.multiselect dd {
|
||||
dl.multiselect,
|
||||
dl.multiselect dt,
|
||||
dl.multiselect dd {
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
#page dl.multiselect select {
|
||||
dl.multiselect select,
|
||||
dl.multiselect dd select {
|
||||
font-size: 12px;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
|
@ -976,12 +986,13 @@ ol.task-list li.done {
|
|||
}
|
||||
.overlay .primary,
|
||||
.overlay #branding h1.page-title,
|
||||
.overlay #page #left,
|
||||
.overlay #page #footer {
|
||||
.overlay #left,
|
||||
.overlay #footer {
|
||||
display: none;
|
||||
}
|
||||
.overlay #page {
|
||||
margin: 0;
|
||||
padding: 0 20px;
|
||||
}
|
||||
.overlay #branding div.breadcrumb {
|
||||
float: left;
|
||||
|
@ -990,13 +1001,10 @@ ol.task-list li.done {
|
|||
}
|
||||
.overlay ul.secondary {
|
||||
background: transparent none;
|
||||
margin: -2.4em 0 0;
|
||||
margin: -2.4em 0 0.5em 0;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
.overlay #content {
|
||||
padding: 0 20px;
|
||||
}
|
||||
.overlay #page {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
@ -1007,8 +1015,8 @@ div.add-or-remove-shortcuts {
|
|||
padding-left: 6px;
|
||||
}
|
||||
|
||||
/* Blocks */
|
||||
|
||||
#page div.block h2 {
|
||||
margin-top:0;
|
||||
/* Dashboard */
|
||||
#dashboard div.block h2 {
|
||||
margin: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ div.vertical-tabs .vertical-tabs-list {
|
|||
width: 25%;
|
||||
float: left;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.vertical-tabs ul li.vertical-tab-button {
|
||||
|
|
Loading…
Reference in New Issue