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

Feat: List URL Hits #88

Merged
merged 14 commits into from
Nov 9, 2023
20 changes: 20 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
changelog:
exclude:
authors:
- dependabot
categories:
- title: Breaking Changes
labels:
- 'breaking change'
- title: Fixed Bugs
labels:
- bug
- title: Enhancements
labels:
- enhancement
- title: Translations
labels:
- lang
- title: New Features
labels:
- 'new feature'
1 change: 0 additions & 1 deletion ChangeLog.md

This file was deleted.

2 changes: 2 additions & 0 deletions app/Config/Boot/development.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
| In development, we want to show as many errors as possible to help
| make sure they don't make it to production. And save us hours of
| painful debugging.
|
| If you set 'display_errors' to '1', CI4's detailed error report will show.
*/
error_reporting(-1);
ini_set('display_errors', '1');
Expand Down
2 changes: 2 additions & 0 deletions app/Config/Boot/production.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
|--------------------------------------------------------------------------
| Don't show ANY in production environments. Instead, let the system catch
| it and display a generic error message.
|
| If you set 'display_errors' to '1', CI4's detailed error report will show.
*/
ini_set('display_errors', '0');
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
Expand Down
6 changes: 6 additions & 0 deletions app/Config/Boot/testing.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<?php

/*
* The environment testing is reserved for PHPUnit testing. It has special
* conditions built into the framework at various places to assist with that.
* You can’t use it for your development.
*/

/*
|--------------------------------------------------------------------------
| ERROR DISPLAY
Expand Down
1 change: 1 addition & 0 deletions app/Config/Routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
$routes->get('edit/(:num)', 'Url::edit/$1', ['filter' => 'session']);
$routes->post('edit/(:num)', 'Url::editAction/$1', ['filter' => 'session']);
$routes->get('hits/(:num)', 'Url::hitslist/$1', ['filter' => 'session']);
$routes->get('hitslistdata/(:num)', 'Url::hitslistData/$1', ['filter' => 'session']); // json hits list for url
$routes->get('qrcode/(:num)', 'Url::generateQRCode/$1', ['filter' => 'session']);
});

Expand Down
2 changes: 1 addition & 1 deletion app/Config/Smarty.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class Smarty extends BaseConfig
{
public $smarty_name = 'SmartyURL';
public $smarty_online_repo = 'https://smartyurl.extendy.net';
public $smarty_version = '0.0.0-dev-DND-1';
public $smarty_version = '0.0.0-dev-dnd.2';

/**
* @var string contain the file name of jquery supported version eg jquery-3.7.1 without js
Expand Down
133 changes: 130 additions & 3 deletions app/Controllers/Url.php
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,6 @@ public function editAction($UrlId)
// updated ok

// i will check tags and update any changes
// samsam
// first of all i will delete all url tags
$delresult = $this->urltagsdatamodel->delUrlTags($url_id);
// now i will enter the tags again
Expand Down Expand Up @@ -736,10 +735,138 @@ public function editAction($UrlId)

public function hitslist($UrlId)
{
echo 'URL HITS OF.' . $UrlId;
if (! auth()->user()->can('url.access', 'admin.manageotherurls', 'super.admin')) {
return smarty_permission_error();
}

$url_id = (int) esc(smarty_remove_whitespace_from_url_identifier($UrlId));
if ($url_id === 0) {
// url_id given is not valid id
return redirect()->to('dashboard')->with('notice', lang('Url.urlError'));
}
$urlData = $this->urlmodel->where('url_id', $url_id)->first();

if ($urlData === null) {
// url not exsists in dataase
return redirect()->to('dashboard')->with('error', lang('Url.urlNotFoundShort'));
}

// i will check the user permission , does he allowed to access this url info
$userCanAccessUrl = $this->smartyurl->userCanAccessUrlInfo($url_id, (int) $urlData['url_user_id']);
if (! $userCanAccessUrl) {
return smarty_permission_error('It not your URL 😉😉😉');
}
$Go_Url = esc(smarty_detect_site_shortlinker() . $urlData['url_identifier']);

$data = [];

$data['lang'] = session('lang');
$data['url_id'] = (int) $urlData['url_id'];
$data['url_identifier'] = esc($urlData['url_identifier']);
$data['url_hitscounter'] = $urlData['url_hitscounter'];
$data['go_url'] = $Go_Url;

return view(smarty_view('url/hitslist'), $data);
}

public function hitslistData($urlId)
{
if (! auth()->user()->can('url.access', 'admin.manageotherurls', 'super.admin')) {
return smarty_permission_error(lang('Common.permissionsNoenoughpermissions'), true);
}

$url_id = (int) $urlId;
$userCanAccessUrl = $this->smartyurl->userCanAccessUrlInfo($url_id);
if (! $userCanAccessUrl) {
return smarty_permission_error('It is not your URL 😉😉😉', true);
}

$draw = $this->request->getGet('draw');
$start = $this->request->getGet('start');
$length = $this->request->getGet('length');

// Do not think that the data that comes from the client is always correct
// so set force max length it maxUrlListPerPage
$system_forcemax_length = setting('Smartyurl.maxUrlListPerPage');
if ($length > setting('Smartyurl.maxUrlListPerPage')) {
$length = $system_forcemax_length;
}

$columnOrder = $this->request->getGet('order');
if ($columnOrder !== null) {
$ajax_column_index = $columnOrder['0']['column'];
$order_by_dir = $columnOrder['0']['dir'];

// Do not think that the data that comes from the client is always correct
// so switch it to use defaults
switch ($order_by_dir) {
case 'asc':
$order_by_rule = 'asc';
break;

case 'desc':
$order_by_rule = 'desc';
break;

default:
$order_by_rule = 'desc';
break;
}

// i will know the column name from get
$ajax_columns = $this->request->getGet('columns');
$order_by_ajax_column_name = $ajax_columns[$ajax_column_index]['name'];

// echo $order_by_ajax_column_name;
// echo $order_by_rule;

switch ($order_by_ajax_column_name) {
case 'hit_date':
$order_by = 'urlhit_at';
break;

default:
$order_by = 'urlhit_urlid';
break;
}
} else {
$order_by = 'urlhit_id';
$order_by_rule = 'desc';
}

$urlAllCount = $this->urlhitsmodel->getHitsByUrlId($url_id, null, null, 'urlhit_urlid', 'desc', false);
$filterAllnumRows = $urlAllCount;
$results = $this->urlhitsmodel->getHitsByUrlId($url_id, $start, $length, $order_by, $order_by_rule, true);
$records = [];
if ($results !== null) {
foreach ($results as $result) {
$records[] = [
'hit_date_col' => $result->urlhit_at,
'hit_ip_col' => $result->urlhit_ip,
'hit_country_col' => $result->urlhit_country,
'hit_device_col' => $result->urlhit_visitordevice,
'hit_useragent_col' => esc($result->urlhit_useragent),
'hit_finalurl_col' => esc($result->urlhit_finaltarget),
];
}
}

$data = [
'draw' => $draw,
'recordsTotal' => $urlAllCount, // $urlAllCount
'recordsFiltered' => $filterAllnumRows, // $filterAllnumRows
'data' => $records,
];

return $this->response->setJSON($data);
}

public function generateQRCode($UrlId)
/**
* This function generates QR Code for tge given URL id
*
* @return mixed
*/
public function generateQRCode(int $UrlId)
{
// set response type
$response = service('response');
Expand Down
3 changes: 2 additions & 1 deletion app/Language/ar/Url.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@
'urlInfoNoRecdirectCondition' => 'لم يتم تعريف شروط اعادة توجيه',
'urlInfoLast25Hits' => 'اخر 25 زيارة للرابط',
'urlInfoSeeAllHits' => 'استعراض جميع الزيارات',
'urlInfoSeeAllHitsforURL' => 'استعراض جميع زيارات الرابط',
'urlInfoVisitDate' => 'التاريخ',
'urlInfoVisitorIP' => 'عنوان الاي بي',
'urlInfoVisitorIP' => 'الاي بي',
'urlInfoVisitorCountry' => 'الدولة',
'urlInfoVisitorDevice' => 'الجهاز',
'urlInfoVisitorUserAgent' => 'الوكيل',
Expand Down
1 change: 1 addition & 0 deletions app/Language/en/Url.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
'urlInfoNoRecdirectCondition' => 'No redirect conditions defined',
'urlInfoLast25Hits' => 'URl Last 25 visits',
'urlInfoSeeAllHits' => 'Show all visits',
'urlInfoSeeAllHitsforURL' => 'Show URL visits',
'urlInfoVisitDate' => 'Date',
'urlInfoVisitorIP' => 'IP',
'urlInfoVisitorCountry' => 'Country',
Expand Down
33 changes: 32 additions & 1 deletion app/Models/UrlHitsModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class UrlHitsModel extends BaseModel
{
protected $DBGroup = 'default';
protected $primaryKey = 'id';
protected $primaryKey = 'urlhit_id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
Expand Down Expand Up @@ -55,6 +55,8 @@ protected function initialize(): void
/**
* get the last 25 hits for URL
*
* @param mixed $UrlId
*
* @return mixed
*/
public function getLast25Hits($UrlId)
Expand All @@ -66,4 +68,33 @@ public function getLast25Hits($UrlId)
->get()
->getResult();
}

/**
* This function gets the URL visit list for a given $urlId which can be a single Url ID or an array of Url IDs.
* $returnType can be:
* - true to return data as an array
* - false to return the row count as an integer without the start and limit
*/
public function getHitsByUrlId(int|array $urlId, int|null $start = null, int|null $length = null, string $orderBy = 'urlhit_urlid', string $orderDirection = 'DESC', bool $returnData = true)
{
$builder = $this->builder();

$builder->orderBy($orderBy, $orderDirection);

if (is_array($urlId)) {
$builder->whereIn('urlhit_urlid', $urlId);
} else {
$builder->where('urlhit_urlid', $urlId);
}

if ($start !== null && $length !== null) {
$builder->limit($length, $start);
}

if ($returnData) {
return $builder->get()->getResult();
}

return $builder->countAllResults();
}
}
Loading