diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..871c647 --- /dev/null +++ b/.github/release.yml @@ -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' diff --git a/ChangeLog.md b/ChangeLog.md deleted file mode 100644 index e5e3a85..0000000 --- a/ChangeLog.md +++ /dev/null @@ -1 +0,0 @@ -# SmartyL \ No newline at end of file diff --git a/app/Config/Boot/development.php b/app/Config/Boot/development.php index 05a8612..aa8099a 100644 --- a/app/Config/Boot/development.php +++ b/app/Config/Boot/development.php @@ -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'); diff --git a/app/Config/Boot/production.php b/app/Config/Boot/production.php index 21d2580..73c7c60 100644 --- a/app/Config/Boot/production.php +++ b/app/Config/Boot/production.php @@ -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); diff --git a/app/Config/Boot/testing.php b/app/Config/Boot/testing.php index e07a1d4..e84670e 100644 --- a/app/Config/Boot/testing.php +++ b/app/Config/Boot/testing.php @@ -1,5 +1,11 @@ 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']); }); diff --git a/app/Config/Smarty.php b/app/Config/Smarty.php index 6892c14..94c6fc6 100644 --- a/app/Config/Smarty.php +++ b/app/Config/Smarty.php @@ -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 diff --git a/app/Controllers/Url.php b/app/Controllers/Url.php index 3c432f1..a2e91e8 100644 --- a/app/Controllers/Url.php +++ b/app/Controllers/Url.php @@ -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 @@ -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'); diff --git a/app/Language/ar/Url.php b/app/Language/ar/Url.php index a888706..6bf700e 100644 --- a/app/Language/ar/Url.php +++ b/app/Language/ar/Url.php @@ -74,8 +74,9 @@ 'urlInfoNoRecdirectCondition' => 'لم يتم تعريف شروط اعادة توجيه', 'urlInfoLast25Hits' => 'اخر 25 زيارة للرابط', 'urlInfoSeeAllHits' => 'استعراض جميع الزيارات', + 'urlInfoSeeAllHitsforURL' => 'استعراض جميع زيارات الرابط', 'urlInfoVisitDate' => 'التاريخ', - 'urlInfoVisitorIP' => 'عنوان الاي بي', + 'urlInfoVisitorIP' => 'الاي بي', 'urlInfoVisitorCountry' => 'الدولة', 'urlInfoVisitorDevice' => 'الجهاز', 'urlInfoVisitorUserAgent' => 'الوكيل', diff --git a/app/Language/en/Url.php b/app/Language/en/Url.php index 4dde5d1..4b13a12 100644 --- a/app/Language/en/Url.php +++ b/app/Language/en/Url.php @@ -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', diff --git a/app/Models/UrlHitsModel.php b/app/Models/UrlHitsModel.php index e103513..762da9d 100644 --- a/app/Models/UrlHitsModel.php +++ b/app/Models/UrlHitsModel.php @@ -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; @@ -55,6 +55,8 @@ protected function initialize(): void /** * get the last 25 hits for URL * + * @param mixed $UrlId + * * @return mixed */ public function getLast25Hits($UrlId) @@ -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(); + } } diff --git a/app/Views/basic/url/hitslist.php b/app/Views/basic/url/hitslist.php new file mode 100644 index 0000000..fb1d907 --- /dev/null +++ b/app/Views/basic/url/hitslist.php @@ -0,0 +1,279 @@ += $this->extend(smarty_view('layout')); ?> + += $this->section('title') ?>= smarty_pagetitle(lang('Url.urlInfoSeeAllHitsforURL')); ?> = $this->endSection() ?> + += $this->section('cssheaderarea') ?> + += $this->endSection() ?> + + + += $this->section('main') ?> + + +
= lang('Url.urlInfoVisitDate'); ?> | += lang('Url.urlInfoVisitorIP'); ?> | += lang('Url.urlInfoVisitorCountry'); ?> | += lang('Url.urlInfoVisitorDevice'); ?> | += lang('Url.urlInfoVisitorUserAgent'); ?> | += lang('Url.urlInfoFinalTarget'); ?> | + +
---|