Skip to content

Commit

Permalink
Merge pull request #5 from jxmot/20210401-mdreport
Browse files Browse the repository at this point in the history
Merge in report functionality
  • Loading branch information
jxmot authored Apr 1, 2021
2 parents 1759ec7 + 8db5e7d commit 4c0f051
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 5 deletions.
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,28 @@ GET http[s]://your-server/path-to-file/mdcountdata.php?id=BLAH
]
```

## Potential Enhancements
## Counter Reports

* Check the IP address of the viewer, if found in a configurable *known IP list* the count is not incremented.
* **NOTE**: When a markdown file with this hit counter is viewed *through* GitHub the IP address will always be within a specific range owned by GitHub.
* One of my other utilites, [getfqdnip](https://github.com/jxmot/getfqdnip) could be part of the solution.

The following files are used in report generation and viewing:

* `mdreport.php` - Retrieves the counter data and renders a Bootstrap 4.x table.
* `mdreport.css` - Additional required CSS for the table
* `mdreport-th.txt` - Column heading text
* `stddefines.php` - A collection of `define()` that make a number of PHP variables available to the application. It contains components used for creating URLs to resources.
* `report.html` - The minimum required HTML/CSS and JavaScript/jQuery to render and display the table.
* **Other Dependencies** : The "tool tips" used on the column headings are created with [tippy.js](<https://atomiks.github.io/tippyjs/>).

**Report Screen Shot :**

<img src="./mdimg/report_sshot.png" style="border: 2px dashed">

The "Hit Count", "Repository" and "Last Counted" headings can be clicked to select sorting criteria and direction (*ascending vs descending*). When a column heading is clicked the report caption will change to reflect the choice.

**NOTES :**
* The links in the "Repository" column and built from the IDs found in `counters.json`. The ID text I used there is the *name of the repository* that the counter is intended for. The corresponding counter data files are named `repo-name`**`_counter.json`**.
* When the report is viewed the data shown is *current*.
* If the "Last Counted" column date and time seem to be incorrect then the time zone may need to be changed. Edit the `timezone.json` file to match your time zone.

## Other Uses

You could count just about anything. All you need is to do a GET of `mdcount.php` with a proper query and you got it!
Expand Down
Binary file added mdimg/report_sshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions mdreport-th.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rank,Hit Count,Repository,Last Counted
35 changes: 35 additions & 0 deletions mdreport.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
div.table-container {
width:80%;
margin:auto;
}

tr th {
text-align:center;
}

tr td {
text-align:center;
}

table caption {
caption-side:top;
text-align:center;
color:black;
font-weight:bold;
}

.sort-arrow-up:after {
font-weight:bold;
content: "\25B2"!important;
}

.sort-arrow-dn:after {
font-weight:bold;
content: "\25BC"!important;
}

.orderhover:hover {
cursor:pointer!important;
background-color: rgba(189, 183, 137, 1);
border-radius: 15px 50px;
}
144 changes: 144 additions & 0 deletions mdreport.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php
// if false run normally, if true then fake the query
define('_DEBUG', false);

// renders and echoes the defines inside of a <p>
define('_DEBUGDEF', false);
require_once './stddefines.php';

// get the configured time zone
function tzone() {
$tmp = json_decode(file_get_contents('./tzone.json'));
return $tmp->tz;
}

// check for debug/test mode
if(!defined('_DEBUG') || _DEBUG === false) {

$mdcountdata = (isset($queries['mdcdata']) ? strtolower($queries['mdcdata']) : THISSRVR.'/mdcountdata.php');

// MUST be done like this for PHP files that are 'linked'
$queries = array();
parse_str($_SERVER['QUERY_STRING'], $queries);
// return all counters, ordered by count
$csort = (isset($queries['csort']) ? strtolower($queries['csort']) : null);
// return all counters, ordered by time of last count
$tsort = (isset($queries['tsort']) ? strtolower($queries['tsort']) : null);
// return all counters, ordered by ID
$isort = (isset($queries['isort']) ? strtolower($queries['isort']) : null);
// for sorts, limit number of counters returned
$limit = (isset($queries['limit']) ? $queries['limit'] : null);
} else {
if(isset($_SERVER['QUERY_STRING'])) {
$q = $_SERVER['QUERY_STRING'];
echo "<p>$q</p>\n";
}

$mdcountdata = THISSRVR.'/mdcountdata.php';

// set as needed for testing
$csort = 'd';
$tsort = null;
$isort = null;
$limit = 2;
}

$thfile = './mdreport-th.txt';
$arrup = 'sort-arrow-up';
$arrdn = 'sort-arrow-dn';
$repohome = 'https://github.com/jxmot/';
$linkmsg = 'Open in New Tab or Window';

if(file_exists($thfile)) {
$fileid = fopen($thfile,'r');
$thdata = fread($fileid,128);
fclose($fileid);
$thitems = explode(',', $thdata);

$sortidx = ($csort !== null ? 1 : ($isort !== null ? 2 : ($tsort !== null ? 3 : -1)));
$sortdir = ($csort !== null ? $csort : ($isort !== null ? $isort : ($tsort !== null ? $tsort : 'a')));

$sorttitle = array(
($sortidx === -1 ? '<h1>BAD Sort Choice!</h1>' : "<i>$thitems[$sortidx]</i>"),
($sortdir === 'a' ? '<i>Ascending</i>' : ($sortdir === 'd' ? '<i>Descending</i>' : '<h1>BAD Sort Direction!</h1>'))
);

$dircss = ($sortdir === 'a' ? $arrup : ($sortdir === 'd' ? $arrdn : ''))
?>
<link rel="stylesheet" href="./mdreport.css?=_<?php echo time(); ?>">
<div class="table-responsive table-container">
<table id="hit-table" class="table table-sm">
<thead>
<?php
for($ix = 0; $ix < count($thitems); $ix++) {
if($ix !== $sortidx) {
echo ' <th'.($ix !== 0 ? ' id="hit-table-col'.$ix.'" class="orderhover" data-ix="'.$ix.'"' : '').'>'.$thitems[$ix].'</th>'."\n";
} else {
echo ' <th id="hit-table-col'.$ix.'" class="orderhover" data-order="'.$sortdir.'" data-ix="'.$ix.'">'.$thitems[$ix].'<span id="hit-table-order'.$ix.'" data-order="'.$sortdir.'" data-ix="'.$ix.'" class="'.$dircss.'">&nbsp;</span></th>'."\n";
}
}
?>
</thead>
<tbody>
<?php
// get(rebuild if in _DEBUG mode) the query string...
$qry = null;
if(!defined('_DEBUG') || _DEBUG === false) {
$qry = '?' . $_SERVER['QUERY_STRING'];
} else {
$qry = '?' . ($csort !== null ? 'csort=' : ($isort !== null ? 'isort=' : ($tsort !== null ? 'tsort=' : 'csort='))) . $sortdir;
if($limit !== null) {
$qry = $qry . '&limit=' . $limit;
}
}

// Create a stream
$opts = array(
'http'=>array(
'method'=>'GET',
'header'=>"Accept-language: en\r\n"
."user-agent: custom\r\n"
."Content-Type: application/json; charset=utf-8\r\n"
."Content-Encoding: text\r\n"
)
);

$context = stream_context_create($opts);
$url = $mdcountdata . ($qry !== null ? $qry : '');
$data = file_get_contents($url, false, $context);
$counters = json_decode($data);

$repqty = count($counters);
$tablecaption = "Top $repqty repositories. Sorted by $sorttitle[0] in $sorttitle[1] order";

for($ix = 0; $ix < $repqty; $ix++) {
echo " <tr>\n";
echo ' <th scope="row">'.($ix+1).'</th>'."\n";
echo " <td>".$counters[$ix]->data->count."</td>\n";
echo ' <td><a target="_blank" href="'.$repohome.$counters[$ix]->id.'" title="'.$linkmsg.'">'.$counters[$ix]->id."</a></td>\n";
$when = ''.$counters[$ix]->data->time;
$dt = new DateTime("@$when", new DateTimeZone(tzone()));
$date = $dt->format('Y/m/d') . '<br>' . $dt->format('H:i:s');
unset($dt);
echo " <td>".$date."</td>\n";
echo " </tr>\n";
}
?>
<!-- BS4 likes to render this on the bottom of the table, but we have CSS that
moves it to the top.
-->
<caption><?php echo $tablecaption; ?></caption>
</tbody>
</table>
</div>
<script>
tippy('.orderhover', {
theme: 'light',
content: 'Click to select or change sorting order.',
});
</script>
<?php
} else { // if(file_exists($thfile))
echo "<h1>Header File was not found - $thfile</h1>\n";
}
?>
83 changes: 83 additions & 0 deletions report.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="description" content="Report generation for the silent hit counter intended for Markdown files. https://github.com/jxmot/markdown-hitcounter"/>
<meta name="author" content="Jim Motyl - https://github.com/jxmot/markdown-hitcounter"/>
<title>My Repositories' Hit Counters</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<!-- https://atomiks.github.io/tippyjs/v6/getting-started/ -->
<script src="https://unpkg.com/@popperjs/core@2"></script>
<script src="https://unpkg.com/tippy.js@6"></script>
<link rel="stylesheet" href="https://unpkg.com/tippy.js@6/animations/scale.css" crossorigin="anonymous">

<!-- https://getbootstrap.com/docs/4.0/getting-started/contents/ -->
<!-- https://popper.js.org/ -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" crossorigin="anonymous"></script>
</head>
<body>
<div id="repout">
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
/*
Send a GET request and invoke a
callback function when completed.
*/
function httpGet(url, callback, tickle = false) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
var resp = this.responseText;
callback(resp);
}
};
// bypass caching, useful when retrieving resources
// that change frequently
if((tickle === true) && (url.includes('?') === false)) {
// ToDo: if url already has a "?" in it then use "&_=" instead
url = url + '?_=' + new Date().getTime();
}
xmlhttp.open('GET', url, true);
xmlhttp.send();
};

//////////////////////////////////////////////////////////////////////////

var sorts = ['?csort=','?isort=','?tsort='];
var sdirs = ['a','d'];
var url = './mdreport.php';
var limit = '&limit=10';
var datasrc = url+sorts[0]+sdirs[0]+limit;

function createTable(elemid, ds) {
httpGet(ds, (resp) => {
$(elemid).html(resp);
$(elemid+' .orderhover').click((col) => {
var target = {
id: col.target.id,
ix: col.target.dataset.ix,
order: col.target.dataset.order
};
$(document).trigger('newtable', target);
});
},false);
};

createTable('#repout', datasrc);

$(document).on('newtable', (e, target) => {
$('.orderhover').off('click');

var datasrc = url+sorts[target.ix - 1] + (target.order === 'a' ? 'd' : 'a') + limit;
$('#repout').html('');
createTable('#repout', datasrc);
});
});
</script>
</body>
</html>

42 changes: 42 additions & 0 deletions stddefines.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
// some of everything, comment out what is NOT being
// used elsewhere.
define('SRVNAME', ((isset($_SERVER['SERVER_NAME']) === true) ? $_SERVER['SERVER_NAME'] : 'none'));
define('SRVPROTO', ((isset($_SERVER['SERVER_PROTOCOL']) === true) ? $_SERVER['SERVER_PROTOCOL'] : 'none'));
define('REMADDR', ((isset($_SERVER['REMOTE_ADDR']) === true) ? $_SERVER['REMOTE_ADDR'] : 'none'));
define('QRYSTR' , ((isset($_SERVER['QUERY_STRING']) === true) ? $_SERVER['QUERY_STRING'] : 'none'));
define('HTTPREF', ((isset($_SERVER['HTTP_REFERER']) === true) ? $_SERVER['HTTP_REFERER'] : 'none'));
define('HTTPHOST', ((isset($_SERVER['HTTP_HOST']) === true) ? $_SERVER['HTTP_HOST'] : 'none'));
define('DOCROOT', ((isset($_SERVER['DOCUMENT_ROOT']) === true) ? $_SERVER['DOCUMENT_ROOT'] : 'none'));
define('PATHTRANS',((isset($_SERVER['PATH_TRANSLATED']) === true) ? $_SERVER['PATH_TRANSLATED'] : 'none'));
define('PHPSELF', ((isset($_SERVER['PHP_SELF']) === true) ? $_SERVER['PHP_SELF'] : 'none'));

// used for assembling URLs to resources as needed
define('HTTPTYPE', ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://'));
define('THISSRVR', HTTPTYPE . SRVNAME . pathinfo(PHPSELF)['dirname']);
define('TRUESELF', THISSRVR . '/' . pathinfo(PHPSELF)['basename']);

// if debug is enabled then show stuff....
if(defined('_DEBUGDEF') && _DEBUGDEF === true) {
echo "\n";
echo "<p><strong>\n";

echo 'SRVNAME : '.SRVNAME."<br>\n";
echo 'SRVPROTO : '.SRVPROTO."<br>\n";
echo 'REMADDR : '.REMADDR."<br>\n";
echo 'QRYSTR : '.QRYSTR."<br>\n";
echo 'HTTPREF : '.HTTPREF."<br>\n";
echo 'HTTPHOST : '.HTTPHOST."<br>\n";
echo 'DOCROOT : '.DOCROOT."<br>\n";
echo 'PATHTRANS: '.PATHTRANS."<br>\n";
echo 'PHPSELF : '.PHPSELF."<br>\n";

echo "<br>\n";
echo 'HTTPTYPE : '.HTTPTYPE."<br>\n";
echo 'THISSRVR : '.THISSRVR."<br>\n";
echo 'TRUESELF : '.TRUESELF."<br>\n";

echo "</strong></p>\n";
echo "\n";
}
?>

0 comments on commit 4c0f051

Please sign in to comment.