Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track internal search queries #242

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions css/dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@

#statify_dashboard .table {
padding-bottom: 12px;
flex: 1 0 50%;
}

#statify_dashboard .table p.sub {
Expand All @@ -86,15 +87,15 @@
overflow: hidden;
}

#statify_dashboard .table.referrer,
#statify_dashboard .table.target {
flex: 1 0 50%;
}

#statify_dashboard .table.total {
flex: 0 0 100%;
}

#statify_dashboard .statify-refresh-button-wrapper {
padding-top: 12px;
width: 100%;
}

#statify_dashboard td {
padding: 8px 0 0;
line-height: 1.4;
Expand Down
78 changes: 69 additions & 9 deletions inc/class-statify-dashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,9 @@ public static function get_stats( $force_refresh = false ) {
/**
* Get stats from DB
*
* @since 0.1.0
* @version 1.4.0
* @since 0.1.0
* @since 1.4.0
* @since 2.0.0 Add search queries to `$data` array.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this common to add a "changelog" like this to PHPDoc? And now we also have three @since and no @version anymore. 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we used the @version wrong, if I am right, it is ment to set versions for APIs and similar (https://docs.phpdoc.org/guide/references/phpdoc/tags/version.html#version), and you can use multiple @since (https://docs.phpdoc.org/guide/references/phpdoc/tags/since.html#since)

Copy link
Contributor

@stklcode stklcode Mar 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, @since is commonly used for changelog purposes, mostly changes to method signature or high-level behavior (at least helpful on public API that can be quite helpful to have this information on hand in the IDE)
@version is meant for semantic changes and the state of the functionality.

Multiple of either one don’t make much sense, because they yield barely any information, just “something was done here“.

Something like this would be more or less meaningful:

@since 1.0 introduced (if this is left empty, the natural assumption would be the same)
@since 1.1 return boolean result instead of void
@since 1.2 make parameter $foo optional with default 42

@version 1.1

I personally often omit the version tags, if there is no real need for that. The since-tags often help even in the same project to see why a method is used in a non-optimal way or similar. In such cases the hint “method was updated in 1.2“ and “we use it since 1.1“ immediately presents a valid reason, so we don’t have to guess what someone may have thought here.

Btw. that is also done in WP core and the changelogs are rendered in the reference articles, e.g.: https://developer.wordpress.org/reference/functions/wp_parse_url/#changelog

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Since" we only use it on some functions and we don't even have a real "public API", would we even mind keeping these @since version numbers up to date?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, a public method is a public method and there are some recommendations out there for things like workarounds or custom triggers that call some of these. Some snippets given by ourselves in the support forums. Documenting API changes enables users of those to potentially migrate their logic without opening another support request, if changes are not self-explaining.

I agree, the majority of our code is designed for internal use and only exposed public for hooks etc.

But as with every project, that’s yet another part of documentation that might get out of sync over time. Though it’s probably one of the easier parts, as old annotations typically never become wrong, so we only have to append another one-liner, if necessary.

*
* @return array DB results
*/
Expand All @@ -322,7 +323,10 @@ private static function _select_data() {
$data = array(
'visits' => $wpdb->get_results(
$wpdb->prepare(
"SELECT `created` as `date`, COUNT(`created`) as `count` FROM `$wpdb->statify` GROUP BY `created` ORDER BY `created` DESC LIMIT %d",
"SELECT `created` as `date`, COUNT(`created`) as `count`
FROM `$wpdb->statify`
GROUP BY `created`
ORDER BY `created` DESC LIMIT %d",
$days_show
),
ARRAY_A
Expand All @@ -332,15 +336,41 @@ private static function _select_data() {
if ( $today ) {
$data['target'] = $wpdb->get_results(
$wpdb->prepare(
"SELECT COUNT(`target`) as `count`, `target` as `url` FROM `$wpdb->statify` WHERE created = %s GROUP BY `target` ORDER BY `count` DESC LIMIT %d",
"SELECT COUNT(`target`) as `count`, `target` as `url`
FROM `$wpdb->statify`
WHERE created = %s AND target NOT LIKE %s
GROUP BY `target`
ORDER BY `count` DESC LIMIT %d",
$current_date,
'/?s%',
$limit
),
ARRAY_A
);
$data['searches'] = $wpdb->get_results(
$wpdb->prepare(
"SELECT COUNT(`target`) as `count`, `target` as `url`
FROM `$wpdb->statify`
WHERE created = %s AND target LIKE %s
GROUP BY `target`
ORDER BY `count` DESC LIMIT %d",
$current_date,
'/?s%',
$limit
),
ARRAY_A
);
$data['referrer'] = $wpdb->get_results(
$wpdb->prepare(
"SELECT COUNT(`referrer`) as `count`, `referrer` as `url`, SUBSTRING_INDEX(SUBSTRING_INDEX(TRIM(LEADING 'www.' FROM(TRIM(LEADING 'https://' FROM TRIM(LEADING 'http://' FROM TRIM(`referrer`))))), '/', 1), ':', 1) as `host` FROM `$wpdb->statify` WHERE `referrer` != '' AND created = %s GROUP BY `host` ORDER BY `count` DESC LIMIT %d",
"SELECT COUNT(`referrer`) as `count`, `referrer` as `url`,
SUBSTRING_INDEX(
SUBSTRING_INDEX(TRIM(LEADING 'www.' FROM(TRIM(LEADING 'https://' FROM TRIM(LEADING 'http://' FROM TRIM(`referrer`))))), '/', 1),
':', 1
) as `host`
FROM `$wpdb->statify`
WHERE `referrer` != '' AND created = %s
GROUP BY `host`
ORDER BY `count` DESC LIMIT %d",
$current_date,
$limit
),
Expand All @@ -349,16 +379,43 @@ private static function _select_data() {
} else {
$data['target'] = $wpdb->get_results(
$wpdb->prepare(
"SELECT COUNT(`target`) as `count`, `target` as `url` FROM `$wpdb->statify` WHERE created > DATE_SUB(%s, INTERVAL %d DAY) GROUP BY `target` ORDER BY `count` DESC LIMIT %d",
"SELECT COUNT(`target`) as `count`, `target` as `url`
FROM `$wpdb->statify`
WHERE created > DATE_SUB(%s, INTERVAL %d DAY) AND target NOT LIKE %s
GROUP BY `target`
ORDER BY `count` DESC LIMIT %d",
$current_date,
$days_show,
'/?s%',
$limit
),
ARRAY_A
);
$data['searches'] = $wpdb->get_results(
$wpdb->prepare(
"SELECT COUNT(`target`) as `count`, `target` as `url`
FROM `$wpdb->statify`
WHERE created > DATE_SUB(%s, INTERVAL %d DAY) AND target LIKE %s
GROUP BY `target`
ORDER BY `count` DESC LIMIT %d",
$current_date,
$days_show,
'/?s%',
$limit
),
ARRAY_A
);
$data['referrer'] = $wpdb->get_results(
$wpdb->prepare(
"SELECT COUNT(`referrer`) as `count`, `referrer` as `url`, SUBSTRING_INDEX(SUBSTRING_INDEX(TRIM(LEADING 'www.' FROM(TRIM(LEADING 'https://' FROM TRIM(LEADING 'http://' FROM TRIM(`referrer`))))), '/', 1), ':', 1) as `host` FROM `$wpdb->statify` WHERE `referrer` != '' AND created > DATE_SUB(%s, INTERVAL %d DAY) GROUP BY `host` ORDER BY `count` DESC LIMIT %d",
"SELECT COUNT(`referrer`) as `count`, `referrer` as `url`,
SUBSTRING_INDEX(
SUBSTRING_INDEX(TRIM(LEADING 'www.' FROM(TRIM(LEADING 'https://' FROM TRIM(LEADING 'http://' FROM TRIM(`referrer`))))), '/', 1),
':', 1
) as `host`
FROM `$wpdb->statify`
WHERE `referrer` != '' AND created > DATE_SUB(%s, INTERVAL %d DAY)
GROUP BY `host`
ORDER BY `count` DESC LIMIT %d",
$current_date,
$days_show,
$limit
Expand All @@ -371,12 +428,15 @@ private static function _select_data() {
$data['visit_totals'] = array(
'today' => $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(`created`) FROM `$wpdb->statify` WHERE created = %s",
"SELECT COUNT(`created`)
FROM `$wpdb->statify`
WHERE created = %s",
$current_date
)
),
'since_beginning' => $wpdb->get_row(
"SELECT COUNT(`created`) AS `count`, MIN(`created`) AS `date` FROM `$wpdb->statify`",
"SELECT COUNT(`created`) AS `count`, MIN(`created`) AS `date`
FROM `$wpdb->statify`",
ARRAY_A
),
);
Expand Down
52 changes: 47 additions & 5 deletions inc/class-statify.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,33 @@ protected static function track( $referrer, $target ) {
// Relative target URL.
$target = user_trailingslashit( str_replace( home_url( '/', 'relative' ), '/', $target ) );

/* Global vars */
// Global vars.
global $wp_rewrite;

// Trim target URL.
if ( $wp_rewrite->permalink_structure ) {
$target = wp_parse_url( $target, PHP_URL_PATH );
// Maybe add trailing slash if that is no search query.
if ( ! self::is_search( $target ) ) {
$target = user_trailingslashit( $target );

// Trim target url.
if ( $wp_rewrite->permalink_structure ) {
$target = wp_parse_url( $target, PHP_URL_PATH );
}
} else {
// Lowercase search query.
$target = strtolower( $target );

// Remove additional query parameters that might exist.
$target = preg_replace( '/^\/\?/', '', $target, -1, $count );

if ( 0 === $count ) {
// If `/search/search query` is used, remove the `/search/`.
$target = urldecode( preg_replace( '/^\/search\/(.*)\/$/', '$1', $target ) );
} else {
// ?s= was used.
wp_parse_str( $target, $tmp );
$target = urldecode( $tmp['s'] );
}
$target = "/?s={$target}";
}

// Init rows.
Expand Down Expand Up @@ -180,6 +201,27 @@ public static function parse_date( $date ) {
return date_i18n( get_option( 'date_format' ), strtotime( $date ) );
}

/**
* Checks URL path/query string for search.
*
* @param string $query_string String with query parameters.
*
* @since 2.0.0
*
* @return bool
*/
public static function is_search( $query_string ) {
if ( 1 === preg_match( '/^\/search\/.+/', $query_string ) ) {
return true;
}

// Remove leading `/?`.
$query_string = preg_replace( '/^\/\?/', '', $query_string );
wp_parse_str( $query_string, $query_params );

return isset( $query_params['s'] ) && ! empty( $query_params['s'] );
}

/**
* Check JavaScript tracking.
*
Expand Down Expand Up @@ -286,7 +328,7 @@ private static function is_bot() {
*/
protected static function is_internal() {
// Skip for preview, 404 calls, feed, search, favicon and sitemap access.
return is_preview() || is_404() || is_feed() || is_search()
return is_preview() || is_404() || is_feed()
|| ( function_exists( 'is_favicon' ) && is_favicon() )
|| '' !== get_query_var( 'sitemap' ) || '' !== get_query_var( 'sitemap-stylesheet' );
}
Expand Down
19 changes: 19 additions & 0 deletions js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
var chartElem = document.getElementById( 'statify_chart' );
var referrerTable = document.querySelector( '#statify_dashboard .table.referrer table tbody' );
var targetTable = document.querySelector( '#statify_dashboard .table.target table tbody' );
var searchesTable = document.querySelector( '#statify_dashboard .table.searches table tbody' );
var totalsTable = document.querySelector( '#statify_dashboard .table.total table tbody' );
var refreshBtn = document.getElementById( 'statify_refresh' );

Expand Down Expand Up @@ -146,6 +147,24 @@
}
}

if ( searchesTable ) {
rows = searchesTable.querySelectorAll( 'tr' );

data.searches.forEach( function( r, idx ) {
row = document.createElement( 'TR' );
row.innerHTML = '<td class="b">' + r.count + '</td>' +
'<td class="t"><a href="' + r.url + '" target="_blank" rel="noopener noreferrer">' + decodeURI( r.url.replace( '/?s=', '' ) ) + '</td>';
if ( rows.length > idx ) {
searchesTable.replaceChild( row, rows[idx] );
} else {
searchesTable.appendChild( row );
}
} );
for ( i = data.searches.length; i < rows.length; i++ ) {
searchesTable.removeChild( rows[i] );
}
}

if ( totalsTable ) {
rows = totalsTable.querySelectorAll( 'tr' );
row = document.createElement( 'TR' );
Expand Down
1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion views/widget-front.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ class_exists( 'Statify' ) || exit;
</table>
</div>

<div class="table searches">
<p class="sub"><?php esc_html_e( 'Top searches', 'statify' ); ?></p>
<table>
<tbody>
<?php for ( $i = 0; $i < $limit; $i++ ) { ?>
<tr class="placeholder"><td colspan="2">&nbsp;</td></tr>
<?php } ?>
Comment on lines +51 to +53
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I get this right, that you first create a table with empty <tr> tags and then fill/replace them using JavaScript? Why not generate the <table> in JavaScript instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, you do just the same as with the other tables 🙈

</tbody>
</table>
</div>

<?php endif; if ( $show_totals ) : ?>
<div class="table total">
<p class="sub"><?php esc_html_e( 'Totals', 'statify' ); ?></p>
Expand All @@ -55,4 +66,6 @@ class_exists( 'Statify' ) || exit;
</div>
<?php endif; ?>

<button type="button" class="button button-primary" id="statify_refresh"><?php esc_html_e( 'Refresh', 'statify' ); ?></button>
<div class="statify-refresh-button-wrapper">
<button type="button" class="button button-primary" id="statify_refresh"><?php esc_html_e( 'Refresh', 'statify' ); ?></button>
</div>