Diff -- /var/www/vhosts/3dshawn.com/ptmatrix.3dshawn.com/activity.cgi

O Operator
Diff

/var/www/vhosts/3dshawn.com/ptmatrix.3dshawn.com/activity.cgi

added on local at 2026-07-01 12:33:52

Added
+175
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 19ad1e935730
Restore this content →
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.
1#!/usr/bin/perl
2
3##################################################################################################################################
4# ______ ____ ____ ______ ______ ____ ___ __ ___ ______ _ __ ____ ____ __ __ _ __ _____ ____
5# / ____// __ \ / __ \ / ____/ / ____// __ \ / | / |/ // ____/| | / // __ \ / __ \ / //_/ | | / /|__ / / __ \
6# / / / / / // / / // __/ / /_ / /_/ // /| | / /|_/ // __/ | | /| / // / / // /_/ // ,< | | / / /_ < / / / /
7# / /___ / /_/ // /_/ // /___ / __/ / _, _// ___ | / / / // /___ | |/ |/ // /_/ // _, _// /| | | |/ / ___/ /_ / /_/ /
8# \____/ \____//_____//_____/ /_/ /_/ |_|/_/ |_|/_/ /_//_____/ |__/|__/ \____//_/ |_|/_/ |_| |___/ /____/(_)\____/
9#
10# - Written by: Shawn Pickering
11# - shawn@shawnpickering.com
12##################################################################################################################################
13use strict;
14use lib '/var/www/vhosts/3dshawn.com/ptmatrix.3dshawn.com';
15
16
17#############################################################
18## Variables
19#############################################################
20
21
22
23############################################################
24# Modules
25############################################################
26use CGI; my $query = new CGI; my $form = $query->Vars;
27use MODS::Login; my $login = new MODS::Login;
28use MODS::PTMatrix::Urls; my $getlist = new MODS::PTMatrix::Urls; my $url = $getlist->urls();
29use MODS::Template; my $tfile = new MODS::Template;
30use MODS::PTMatrix::Wrapper; my $load = new MODS::PTMatrix::Wrapper;
31use MODS::DBConnect; my $db = new MODS::DBConnect;
32use MODS::PTMatrix::Config; my $config = new MODS::PTMatrix::Config; my $database_name = $config->settings('database_name');
33use MODS::PTMatrix::PM;
34
35
36
37#############################################################
38## Get form data
39#############################################################
40foreach my $line(keys %$form){
41 $form->{$line} =~ s/[^!-~\s]//g;
42 $form->{$line} = quotemeta("$form->{$line}");
43}
44
45
46
47############################################################
48# Program Controls
49############################################################
50$|=1;
51my $userinfo = $login->login_verify();
52if(!$userinfo){print qq~Content-type:text/html; Cache-Control:no-cache;\n\n<script>window.location="$url->{login}";</script>~;}
53elsif(($form->{format} || '') eq 'csv'){&export_csv;}
54else{&activity_page;}
55
56
57############################################################
58# Subroutines
59############################################################
60sub range_dates{
61 #Range defaults to this month, matches reports.cgi / cycle_time.cgi / portfolio.cgi
62 my @lt = localtime;
63 my $today_iso = sprintf("%04d-%02d-%02d", $lt[5]+1900, $lt[4]+1, $lt[3]);
64 my $month_iso = sprintf("%04d-%02d-01", $lt[5]+1900, $lt[4]+1);
65 my $rs = $form->{start} || $month_iso;
66 my $re = $form->{end} || $today_iso;
67 $rs = $month_iso unless $rs =~ /^\d{4}-\d{2}-\d{2}$/;
68 $re = $today_iso unless $re =~ /^\d{4}-\d{2}-\d{2}$/;
69 return ($rs, $re);
70}
71
72sub activity_page{
73 my $cid = MODS::PTMatrix::PM::get_company_id($userinfo);
74 my ($range_start, $range_end) = &range_dates;
75 my $range_end_eod = "$range_end 23:59:59";
76
77 my $dbh = $db->db_connect();
78
79 #------------------------------------------------------------------------------------------------------
80 #Last 200 audit rows in the range, joined to the actor user
81 my $sql = qq~SELECT a.id, a.action, a.target_type, a.target_id, a.occurred_at, a.user_id, u.display_name AS actor_name, u.avatar_color FROM `${database_name}`.audit_log a LEFT JOIN `${database_name}`.users u ON u.id = a.user_id WHERE a.company_id='$cid' AND a.occurred_at BETWEEN '$range_start' AND '$range_end_eod' ORDER BY a.occurred_at DESC LIMIT 200~;
82 my @rows = $db->db_readwrite_multiple($dbh, $sql, $ENV{SCRIPT_NAME}, __LINE__);
83 #------------------------------------------------------------------------------------------------------
84
85 $db->db_disconnect($dbh);
86
87 foreach my $r(@rows){
88 $r->{actor_initials} = $r->{actor_name} ? MODS::PTMatrix::PM::initials($r->{actor_name}) : '?';
89 $r->{avatar_color} ||= '#5aa9ff';
90 $r->{action_label} = &action_label($r->{action});
91 if ($r->{target_type} eq 'task') {$r->{target_url} = "/task.cgi?id=$r->{target_id}";}
92 elsif($r->{target_type} eq 'project') {$r->{target_url} = "/project.cgi?id=$r->{target_id}";}
93 else {$r->{target_url} = '';}
94 }
95
96 print qq~Content-type:text/html; Cache-Control:no-cache;\n\n~;
97
98 my $is_mgr = MODS::PTMatrix::PM::is_manager_or_admin($userinfo) ? 1 : 0;
99
100 #Create all template variables
101 my $tvars;
102 $tvars->{range_start} = $range_start;
103 $tvars->{range_end} = $range_end;
104 $tvars->{rows} = \@rows;
105 $tvars->{has_rows} = scalar(@rows) ? 1 : 0;
106 $tvars->{is_mgr} = $is_mgr;
107
108 my $body = join('', $tfile->template('tf_activity.html', $tvars, $userinfo));
109
110 $load->render({
111 userinfo => $userinfo,
112 page_key => 'activity',
113 title => 'Activity Feed',
114 body => $body,
115 });
116}
117
118sub export_csv{
119 #CSV export - Business+ plan gate, admin/manager only
120 if(!MODS::PTMatrix::PM::is_manager_or_admin($userinfo)){
121 print qq~Status: 403 Forbidden\nContent-Type: text/plain\n\nManagers only\n~;
122 return;
123 }
124
125 my $cid = MODS::PTMatrix::PM::get_company_id($userinfo);
126 my $dbh = $db->db_connect();
127
128 my $plan = $db->db_readwrite($dbh, qq~SELECT plan_tier FROM `${database_name}`.companies WHERE id='$cid'~, $ENV{SCRIPT_NAME}, __LINE__);
129 my $tier = $plan ? ($plan->{plan_tier} || 'trial') : 'trial';
130 if(!($tier eq 'business' || $tier eq 'enterprise' || $tier eq 'trial' || $userinfo->{is_super_admin})){
131 $db->db_disconnect($dbh);
132 print qq~Status: 402 Payment Required\nContent-Type: text/plain\n\nCSV export is on the Business plan and up. <a href="$url->{billing}">Upgrade</a>\n~;
133 return;
134 }
135
136 my ($range_start, $range_end) = &range_dates;
137 my $range_end_eod = "$range_end 23:59:59";
138
139 my $sql = qq~SELECT a.id, a.action, a.target_type, a.target_id, a.occurred_at, a.ip_addr, u.email AS actor_email, u.display_name AS actor_name FROM `${database_name}`.audit_log a LEFT JOIN `${database_name}`.users u ON u.id = a.user_id WHERE a.company_id='$cid' AND a.occurred_at BETWEEN '$range_start' AND '$range_end_eod' ORDER BY a.occurred_at DESC LIMIT 50000~;
140 my @rows_csv = $db->db_readwrite_multiple($dbh, $sql, $ENV{SCRIPT_NAME}, __LINE__);
141 $db->db_disconnect($dbh);
142
143 print qq~Content-Type: text/csv; charset=utf-8\n~;
144 print qq~Content-Disposition: attachment; filename="audit_log_${cid}.csv"\n\n~;
145 print "id,occurred_at,actor_email,actor_name,action,target_type,target_id,ip\n";
146 foreach my $r(@rows_csv){
147 my @cols = ($r->{id}, $r->{occurred_at}, $r->{actor_email}||'', $r->{actor_name}||'', $r->{action}, $r->{target_type}||'', $r->{target_id}||'', $r->{ip_addr}||'');
148 my @esc = map {
149 my $s = defined $_ ? $_ : '';
150 if($s =~ /[",\n\r]/){$s =~ s/"/""/g; $s = qq("$s");}
151 $s;
152 } @cols;
153 print join(',', @esc), "\n";
154 }
155}
156
157#Friendly label for the action code
158sub action_label{
159 my($a) = @_;
160 my %M = (
161 'task.create' => 'created task',
162 'task.update' => 'updated task',
163 'task.move' => 'moved task',
164 'task.assign' => 'assigned task',
165 'task.comment' => 'commented on task',
166 'task.delete' => 'deleted task',
167 'project.create' => 'created project',
168 'project.update' => 'updated project',
169 'project.archive'=> 'archived project',
170 'project.delete' => 'deleted project',
171 'user.invite' => 'invited user',
172 'user.remove' => 'removed user',
173 );
174 return $M{$a} || $a;
175}