AI Verified

Name

Simple Project Timer

About

<p>This snippet provides a project timer functionality within the WordPress admin dashboard. It allows users to:<br />- Start, stop, and log time spent on various tasks.<br />- Manually add time entries with comments.<br />- View a log of all time entries, including the date, time spent, comment, and user.<br />- Clear the entire log of entries.<br />- Calculate and display the total time spent on the project.</p>

Language

PHP

Rating

Voted: 0 by 0 user(s)

Codevault

Super-Snippet-Storage

Scroll down to see more snippets from this codevault.

Wordpress Compatability

The author has indicated that this snippet is compatable up to wordpress version: Not Specified

Our AI bot has checked this snippet is compatable up to wordpress version: 6.1

Code Snippet Plugin Sync

Free & Pro

Download this snippet by clicking the download button, then head over to the Code Snippet Plugin settings in your wordpress admin dashboard, select the import menu then upload this file to import into your wordpress site.

Pro Only (Coming Soon)

You will be able to click a button and sync this snippet to your wordpress site automatically and from your dashboard manage all code snippets across all your wordpress sites that have the Code Snippets Pro plugin installed.

History

Last modified:

28/06/2024

Important Note

This snippet has the following status:

AI Verified

This snippet has been tested by our AI bot, see any comments below.

AI Bot Comments:

Potential vulnerability found : SQL Injection
Found on line : 694
Code : ->query("TRUNCATE TABLE $table_name")
Vulnerable line : 44
Code : $table_name = $wpdb->prefix . 'project_timer');

Found 1 vulnerabilities

Simple Project Timer

 
                    
1/*
2Snippet Name: Simple Project Timer
3Description: A simple project timer snippet for wp-admin using a custom table
4Author: Mark Harris
5Company: Christchurch Web Solutions
6URI: https://www.christchurchwebsolutions.co.uk
7 
8This snippet provides a project timer functionality within the WordPress admin dashboard. It allows users to:
9- Start, stop, and log time spent on various tasks.
10- Manually add time entries with comments.
11- View a log of all time entries, including the date, time spent, comment, and user.
12- Clear the entire log of entries.
13- Calculate and display the total time spent on the project.
14 
15Main Features:
161. Timer Functionality: Start and stop a timer to track the duration of tasks.
172. Manual Time Entry: Add time entries manually, specifying the date, time spent, comment, and user.
183. Log Display: View a detailed log of all time entries in a table format.
194. Clear Log: Clear all log entries and reset the total time.
205. Database Integration: Use a custom database table to store log entries and total time.
216. AJAX Requests: Handle all data operations (save, fetch, delete, update) via AJAX for seamless user experience.
22 
23Database Schema:
24- Table Name: wp_project_timer
25- Columns:
26 - id (INT): Primary key, auto-incremented.
27 - date (DATETIME): The date and time when the entry was made.
28 - time_spent (TIME): The duration of time spent.
29 - comment (TEXT): A comment describing the task or entry.
30 - user (VARCHAR): The user who made the entry.
31 
32AJAX Handlers:
33- save_project_timer_entry: Save a new time entry to the database.
34- fetch_project_timer_entries: Fetch all time entries from the database.
35- fetch_project_timer_total_time: Fetch the total time spent from the database.
36- delete_project_timer_entry: Delete a specific time entry from the database.
37- update_project_timer_total_time: Update the total time spent in the database.
38- clear_project_timer_log: Clear all time entries from the database and reset the total time.
39*/
40 
41// Ensure the custom table is created
42function create_project_timer_table() {
43 global $wpdb;
44 $table_name = $wpdb->prefix . 'project_timer';
45 $charset_collate = $wpdb->get_charset_collate();
46 
47 if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) != $table_name) {
48 $sql = "CREATE TABLE $table_name (
49 id mediumint(9) NOT NULL AUTO_INCREMENT,
50 date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
51 time_spent time NOT NULL,
52 comment text NOT NULL,
53 user varchar(100) NOT NULL,
54 PRIMARY KEY (id)
55 ) $charset_collate;";
56 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
57 dbDelta($sql);
58 }
59}
60create_project_timer_table();
61 
62// Hook to add admin menu
63add_action('admin_menu', 'project_timer_menu');
64 
65function project_timer_menu() {
66 add_menu_page('Project Timer', 'Project Timer', 'manage_options', 'project-timer', 'project_timer_page', 'dashicons-clock', 6);
67}
68 
69function project_timer_page() {
70 $current_user = wp_get_current_user();
71 ?>
72 <div class="wrap">
73 <h1>Project Timer</h1>
74 <div id="stopwatch">
75 <span id="timer">00:00:00</span>
76 </div>
77 <div id="buttonContainer">
78 <button id="startBtn" class="button button-primary">Start</button>
79 <button id="stopBtn" class="button button-secondary">Stop</button>
80 <button id="clearLogBtn" class="button button-danger">Clear Log</button>
81 <button id="manualEntryBtn" class="button button-primary">Add Manual Entry</button>
82 <button id="copyClipboardBtn" class="button button-secondary">Copy to Clipboard</button>
83 </div>
84 <h2>Time Log</h2>
85 <table id="timeLog" class="widefat">
86 <thead>
87 <tr>
88 <th>Date</th>
89 <th>Time Spent</th>
90 <th>Comment</th>
91 <th>User</th>
92 <th>Action</th>
93 </tr>
94 </thead>
95 <tbody>
96 <!-- Log entries will be appended here -->
97 </tbody>
98 </table>
99 <h2>Total Time Spent on the Project: <span id="totalTime">00:00:00</span></h2>
100 </div>
101 <div id="manualEntryModal" class="modal">
102 <div class="modal-content">
103 <span class="close">&times;</span>
104 <h2>Add Manual Entry</h2>
105 <form id="manualEntryForm">
106 <label for="manualDate">Date:</label>
107 <input type="date" id="manualDate" name="manualDate" required><br><br>
108 <label for="manualTime">Time (hh:mm:ss):</label>
109 <input type="text" id="manualTime" name="manualTime" pattern="\d{2}:\d{2}:\d{2}" required><br><br>
110 <label for="manualComment">Comment:</label>
111 <textarea id="manualComment" name="manualComment" required></textarea><br><br>
112 <label for="manualUser">Username:</label>
113 <input type="text" id="manualUser" name="manualUser" required value="<?php echo $current_user->display_name; ?>"><br><br>
114 <input type="submit" class="button button-primary" value="Add Entry">
115 </form>
116 </div>
117 </div>
118 <script type="text/javascript">
119 document.addEventListener('DOMContentLoaded', function() {
120 let startTime;
121 let updatedTime;
122 let difference;
123 let tInterval;
124 let running = false;
125 let totalTime = 0;
126 
127 const timerDisplay = document.getElementById('timer');
128 const startBtn = document.getElementById('startBtn');
129 const stopBtn = document.getElementById('stopBtn');
130 const clearLogBtn = document.getElementById('clearLogBtn');
131 const manualEntryBtn = document.getElementById('manualEntryBtn');
132 const copyClipboardBtn = document.getElementById('copyClipboardBtn');
133 const manualEntryModal = document.getElementById('manualEntryModal');
134 const closeModal = document.getElementsByClassName('close')[0];
135 const manualEntryForm = document.getElementById('manualEntryForm');
136 const timeLog = document.getElementById('timeLog').getElementsByTagName('tbody')[0];
137 const totalTimeDisplay = document.getElementById('totalTime');
138 const currentUser = '<?php echo $current_user->display_name; ?>';
139 
140 // Load log entries and total time from database
141 fetchLogEntries();
142 fetchTotalTime();
143 
144 // Check if timer was running
145 if (localStorage.getItem('running') === 'true') {
146 startTime = parseInt(localStorage.getItem('startTime'), 10);
147 running = true;
148 tInterval = setInterval(updateTime, 1000);
149 } else {
150 startTime = null;
151 }
152 
153 startBtn.addEventListener('click', startTimer);
154 stopBtn.addEventListener('click', stopTimer);
155 clearLogBtn.addEventListener('click', clearLog);
156 manualEntryBtn.onclick = function() {
157 manualEntryModal.style.display = "block";
158 }
159 
160 closeModal.onclick = function() {
161 manualEntryModal.style.display = "none";
162 }
163 
164 window.onclick = function(event) {
165 if (event.target == manualEntryModal) {
166 manualEntryModal.style.display = "none";
167 }
168 }
169 
170 manualEntryForm.onsubmit = function(event) {
171 event.preventDefault();
172 const manualDate = new Date(document.getElementById('manualDate').value);
173 const formattedDate = manualDate.toISOString().slice(0, 19).replace('T', ' ');
174 const manualTime = document.getElementById('manualTime').value;
175 const manualComment = document.getElementById('manualComment').value;
176 const manualUser = document.getElementById('manualUser').value;
177 
178 addLogEntry(formattedDate, manualTime, manualComment, manualUser, true);
179 saveLogEntry(formattedDate, manualTime, manualComment, manualUser);
180 updateTotalTime(manualTime, true);
181 manualEntryModal.style.display = "none";
182 }
183 
184 copyClipboardBtn.addEventListener('click', function() {
185 const table = document.getElementById('timeLog');
186 const range = document.createRange();
187 range.selectNode(table);
188 window.getSelection().removeAllRanges();
189 window.getSelection().addRange(range);
190 
191 try {
192 document.execCommand('copy');
193 alert('Table copied to clipboard!');
194 } catch (err) {
195 console.error('Error copying table:', err);
196 }
197 
198 window.getSelection().removeAllRanges();
199 });
200 
201 function fetchLogEntries() {
202 fetch(ajaxurl, {
203 method: 'POST',
204 headers: {
205 'Content-Type': 'application/x-www-form-urlencoded',
206 },
207 body: new URLSearchParams({
208 action: 'fetch_project_timer_entries',
209 _wpnonce: '<?php echo wp_create_nonce('fetch_project_timer_entries'); ?>'
210 })
211 })
212 .then(response => response.json())
213 .then(data => {
214 if (data.success && data.data && Array.isArray(data.data.entries)) {
215 initializeLogEntries(data.data.entries);
216 }
217 })
218 .catch(error => {
219 console.error('Error fetching log entries:', error);
220 });
221 }
222 
223 function fetchTotalTime() {
224 fetch(ajaxurl, {
225 method: 'POST',
226 headers: {
227 'Content-Type': 'application/x-www-form-urlencoded',
228 },
229 body: new URLSearchParams({
230 action: 'fetch_project_timer_total_time',
231 _wpnonce: '<?php echo wp_create_nonce('fetch_project_timer_total_time'); ?>'
232 })
233 })
234 .then(response => response.json())
235 .then(data => {
236 if (data.success && data.data !== undefined) {
237 totalTime = parseInt(data.data.total_time, 10); // Ensure total time is an integer
238 updateTotalTimeDisplay();
239 }
240 })
241 .catch(error => {
242 console.error('Error fetching total time:', error);
243 });
244 }
245 
246 function initializeLogEntries(entries) {
247 if (!entries || !Array.isArray(entries)) {
248 return;
249 }
250 entries.forEach(entry => {
251 addLogEntry(entry.date, entry.time_spent, entry.comment, entry.user);
252 });
253 }
254 
255 function startTimer() {
256 if (!running) {
257 startTime = new Date().getTime();
258 localStorage.setItem('startTime', startTime);
259 localStorage.setItem('running', true);
260 tInterval = setInterval(updateTime, 1000);
261 running = true;
262 }
263 }
264 
265 function updateTime() {
266 updatedTime = new Date().getTime();
267 difference = updatedTime - startTime;
268 
269 let hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
270 let minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60));
271 let seconds = Math.floor((difference % (1000 * 60)) / 1000);
272 
273 hours = (hours < 10) ? "0" + hours : hours;
274 minutes = (minutes < 10) ? "0" + minutes : minutes;
275 seconds = (seconds < 10) ? "0" + seconds : seconds;
276 
277 timerDisplay.innerHTML = hours + ':' + minutes + ':' + seconds;
278 }
279 
280 function stopTimer() {
281 if (running) {
282 clearInterval(tInterval);
283 running = false;
284 const elapsedTime = timerDisplay.innerHTML;
285 const comment = prompt('Please enter a comment for this time entry:');
286 const now = new Date();
287 const dateString = now.toISOString().slice(0, 19).replace('T', ' ');
288 
289 addLogEntry(dateString, elapsedTime, comment, currentUser, true);
290 saveLogEntry(dateString, elapsedTime, comment, currentUser);
291 updateTotalTime(elapsedTime, true);
292 localStorage.setItem('running', false);
293 }
294 }
295 
296 function addLogEntry(date, time, comment, user, prepend = false) {
297 const newRow = document.createElement('tr');
298 const dateCell = newRow.insertCell(0);
299 const timeCell = newRow.insertCell(1);
300 const commentCell = newRow.insertCell(2);
301 const userCell = newRow.insertCell(3);
302 const deleteCell = newRow.insertCell(4);
303 
304 dateCell.textContent = date;
305 timeCell.textContent = time;
306 commentCell.textContent = comment;
307 userCell.textContent = user;
308 
309 const deleteBtn = document.createElement('button');
310 deleteBtn.textContent = 'Delete';
311 deleteBtn.className = 'button button-secondary deleteBtn';
312 deleteBtn.onclick = function () {
313 deleteLogEntry(newRow, date, time, comment, user);
314 };
315 deleteCell.appendChild(deleteBtn);
316 
317 if (prepend) {
318 timeLog.prepend(newRow);
319 } else {
320 timeLog.appendChild(newRow);
321 }
322 }
323 
324 function deleteLogEntry(row, date, time, comment, user) {
325 if (confirm('Are you sure you want to delete this entry?')) {
326 row.parentNode.removeChild(row);
327 
328 fetch(ajaxurl, {
329 method: 'POST',
330 headers: {
331 'Content-Type': 'application/x-www-form-urlencoded',
332 },
333 body: new URLSearchParams({
334 action: 'delete_project_timer_entry',
335 date: date,
336 time_spent: time,
337 comment: comment,
338 user: user,
339 _wpnonce: '<?php echo wp_create_nonce('delete_project_timer_entry'); ?>'
340 })
341 })
342 .then(response => {
343 if (!response.ok) {
344 throw new Error('Network response was not ok');
345 }
346 return response.json();
347 })
348 .then(data => {
349 if (data.success) {
350 console.log('Entry deleted successfully');
351 } else {
352 console.error('Failed to delete entry:', data);
353 }
354 })
355 .catch(error => {
356 console.error('Error deleting entry:', error);
357 });
358 
359 const timeParts = time.split(':');
360 const hours = parseInt(timeParts[0], 10);
361 const minutes = parseInt(timeParts[1], 10);
362 const seconds = parseInt(timeParts[2], 10);
363 totalTime -= (hours * 3600) + (minutes * 60) + seconds;
364 updateDatabaseTotalTime(totalTime); // Update total time in database
365 updateTotalTimeDisplay();
366 }
367 }
368 
369 function saveLogEntry(date, time, comment, user) {
370 fetch(ajaxurl, {
371 method: 'POST',
372 headers: {
373 'Content-Type': 'application/x-www-form-urlencoded',
374 },
375 body: new URLSearchParams({
376 action: 'save_project_timer_entry',
377 date: date,
378 time_spent: time,
379 comment: comment,
380 user: user,
381 _wpnonce: '<?php echo wp_create_nonce('save_project_timer_entry'); ?>'
382 })
383 })
384 .then(response => {
385 if (!response.ok) {
386 throw new Error('Network response was not ok');
387 }
388 return response.json();
389 })
390 .then(data => {
391 if (data.success) {
392 console.log('Entry saved successfully');
393 } else {
394 console.error('Failed to save entry:', data);
395 }
396 })
397 .catch(error => {
398 console.error('Error saving entry:', error);
399 });
400 }
401 
402 function updateTotalTime(time, add) {
403 const timeParts = time.split(':');
404 const hours = parseInt(timeParts[0], 10);
405 const minutes = parseInt(timeParts[1], 10);
406 const seconds = parseInt(timeParts[2], 10);
407 
408 const timeInSeconds = (hours * 3600) + (minutes * 60) + seconds;
409 totalTime = add ? totalTime + timeInSeconds : totalTime - timeInSeconds;
410 
411 updateDatabaseTotalTime(totalTime);
412 updateTotalTimeDisplay();
413 }
414 
415 function updateDatabaseTotalTime(time) {
416 fetch(ajaxurl, {
417 method: 'POST',
418 headers: {
419 'Content-Type': 'application/x-www-form-urlencoded',
420 },
421 body: new URLSearchParams({
422 action: 'update_project_timer_total_time',
423 total_time: time,
424 _wpnonce: '<?php echo wp_create_nonce('update_project_timer_total_time'); ?>'
425 })
426 })
427 .then(response => {
428 if (!response.ok) {
429 throw new Error('Network response was not ok');
430 }
431 return response.json();
432 })
433 .then(data => {
434 if (data.success) {
435 console.log('Total time updated successfully');
436 } else {
437 console.error('Failed to update total time:', data);
438 }
439 })
440 .catch(error => {
441 console.error('Error updating total time:', error);
442 });
443 }
444 
445 function updateTotalTimeDisplay() {
446 const totalHours = Math.floor(totalTime / 3600);
447 const totalMinutes = Math.floor((totalTime % 3600) / 60);
448 const totalSeconds = totalTime % 60;
449 
450 const formattedHours = (totalHours < 10) ? "0" + totalHours : totalHours;
451 const formattedMinutes = (totalMinutes < 10) ? "0" + totalMinutes : totalMinutes;
452 const formattedSeconds = (totalSeconds < 10) ? "0" + totalSeconds : totalSeconds;
453 
454 totalTimeDisplay.innerHTML = formattedHours + ':' + formattedMinutes + ':' + formattedSeconds;
455 }
456 
457 function clearLog() {
458 if (confirm('Are you sure you want to clear the log? This action cannot be undone.')) {
459 fetch(ajaxurl, {
460 method: 'POST',
461 headers: {
462 'Content-Type': 'application/x-www-form-urlencoded',
463 },
464 body: new URLSearchParams({
465 action: 'clear_project_timer_log',
466 _wpnonce: '<?php echo wp_create_nonce('clear_project_timer_log'); ?>'
467 })
468 })
469 .then(response => response.json())
470 .then(data => {
471 if (data.success) {
472 console.log('Log cleared successfully');
473 timeLog.innerHTML = ''; // Clear UI table
474 totalTime = 0; // Reset total time
475 updateTotalTimeDisplay(); // Update UI total time
476 } else {
477 console.error('Failed to clear log:', data);
478 }
479 })
480 .catch(error => {
481 console.error('Error clearing log:', error);
482 });
483 }
484 }
485 });
486 </script>
487 <style>
488 body {
489 font-family: Arial, sans-serif;
490 }
491 #stopwatch {
492 font-size: 2em;
493 margin: 20px 0;
494 padding: 10px;
495 background-color: #f0f0f0;
496 border: 1px solid #ddd;
497 border-radius: 5px;
498 display: inline-block;
499 }
500 #buttonContainer {
501 margin-top: 10px;
502 }
503 #startBtn, #stopBtn, #clearLogBtn, #manualEntryBtn, #copyClipboardBtn {
504 margin-right: 10px;
505 margin-top: 10px;
506 }
507 #timeLog {
508 margin-top: 20px;
509 width: 100%;
510 border-collapse: collapse;
511 box-shadow: 0 2px 3px rgba(0,0,0,0.1);
512 }
513 #timeLog th, #timeLog td {
514 border: 1px solid #ddd;
515 padding: 8px;
516 text-align: left;
517 }
518 #timeLog th {
519 background-color: #f8f8f8;
520 }
521 #totalTime {
522 font-weight: bold;
523 font-size: 1.2em;
524 margin-top: 20px;
525 display: inline-block;
526 }
527 .modal {
528 display: none;
529 position: fixed;
530 z-index: 1;
531 padding-top: 100px;
532 left: 0;
533 top: 0;
534 width: 100%;
535 height: 100%;
536 overflow: auto;
537 background-color: rgb(0,0,0);
538 background-color: rgba(0,0,0,0.4);
539 }
540 .modal-content {
541 background-color: #fff;
542 margin: auto;
543 padding: 20px;
544 border: 1px solid #888;
545 width: 80%;
546 border-radius: 5px;
547 box-shadow: 0 5px 15px rgba(0,0,0,0.3);
548 }
549 .close {
550 color: #aaa;
551 float: right;
552 font-size: 28px;
553 font-weight: bold;
554 }
555 .close:hover,
556 .close:focus {
557 color: black;
558 text-decoration: none;
559 cursor: pointer;
560 }
561 .modal-content h2 {
562 border-bottom: 2px solid #ddd;
563 padding-bottom: 10px;
564 margin-bottom: 20px;
565 }
566 .modal-content label {
567 font-weight: bold;
568 margin-right: 10px;
569 }
570 .modal-content input[type="text"],
571 .modal-content input[type="date"],
572 .modal-content textarea {
573 width: calc(100% - 20px);
574 padding: 10px;
575 margin-bottom: 20px;
576 border: 1px solid #ddd;
577 border-radius: 4px;
578 }
579 .modal-content textarea {
580 resize: vertical;
581 }
582 .modal-content input[type="submit"] {
583 padding: 10px 20px;
584 border: none;
585 background-color: #0073aa;
586 color: white;
587 border-radius: 4px;
588 cursor: pointer;
589 }
590 .modal-content input[type="submit"]:hover {
591 background-color: #005a87;
592 }
593 </style>
594 <?php
595}
596 
597// AJAX handlers
598add_action('wp_ajax_save_project_timer_entry', 'save_project_timer_entry');
599add_action('wp_ajax_fetch_project_timer_entries', 'fetch_project_timer_entries');
600add_action('wp_ajax_fetch_project_timer_total_time', 'fetch_project_timer_total_time');
601add_action('wp_ajax_delete_project_timer_entry', 'delete_project_timer_entry');
602add_action('wp_ajax_update_project_timer_total_time', 'update_project_timer_total_time');
603add_action('wp_ajax_clear_project_timer_log', 'clear_project_timer_log');
604 
605function save_project_timer_entry() {
606 check_ajax_referer('save_project_timer_entry', '_wpnonce');
607 
608 if (isset($_POST['date'], $_POST['time_spent'], $_POST['comment'], $_POST['user'])) {
609 global $wpdb;
610 $table_name = $wpdb->prefix . 'project_timer';
611 
612 $date = sanitize_text_field($_POST['date']);
613 $time_spent = sanitize_text_field($_POST['time_spent']);
614 $comment = sanitize_textarea_field($_POST['comment']);
615 $user = sanitize_text_field($_POST['user']);
616 
617 $wpdb->insert($table_name, [
618 'date' => $date,
619 'time_spent' => $time_spent,
620 'comment' => $comment,
621 'user' => $user
622 ]);
623 
624 wp_send_json_success();
625 } else {
626 wp_send_json_error();
627 }
628}
629 
630function fetch_project_timer_entries() {
631 check_ajax_referer('fetch_project_timer_entries', '_wpnonce');
632 
633 global $wpdb;
634 $table_name = $wpdb->prefix . 'project_timer';
635 
636 $entries = $wpdb->get_results("SELECT * FROM $table_name ORDER BY date DESC", ARRAY_A);
637 
638 if (!empty($entries)) {
639 wp_send_json_success(['entries' => $entries]);
640 } else {
641 wp_send_json_success(['entries' => []]); // Return an empty array if no entries
642 }
643}
644 
645function fetch_project_timer_total_time() {
646 check_ajax_referer('fetch_project_timer_total_time', '_wpnonce');
647 
648 $total_time = get_option('project_timer_total_time', 0);
649 
650 wp_send_json_success(['total_time' => $total_time]);
651}
652 
653function delete_project_timer_entry() {
654 check_ajax_referer('delete_project_timer_entry', '_wpnonce');
655 
656 if (isset($_POST['date'], $_POST['time_spent'], $_POST['comment'], $_POST['user'])) {
657 global $wpdb;
658 $table_name = $wpdb->prefix . 'project_timer';
659 
660 $date = sanitize_text_field($_POST['date']);
661 $time_spent = sanitize_text_field($_POST['time_spent']);
662 $comment = sanitize_textarea_field($_POST['comment']);
663 $user = sanitize_text_field($_POST['user']);
664 
665 $wpdb->delete($table_name, [
666 'date' => $date,
667 'time_spent' => $time_spent,
668 'comment' => $comment,
669 'user' => $user
670 ]);
671 
672 wp_send_json_success();
673 } else {
674 wp_send_json_error();
675 }
676}
677 
678function update_project_timer_total_time() {
679 check_ajax_referer('update_project_timer_total_time', '_wpnonce');
680 
681 if (isset($_POST['total_time'])) {
682 $total_time = intval($_POST['total_time']);
683 update_option('project_timer_total_time', $total_time);
684 wp_send_json_success();
685 } else {
686 wp_send_json_error();
687 }
688}
689 
690function clear_project_timer_log() {
691 check_ajax_referer('clear_project_timer_log', '_wpnonce');
692 
693 global $wpdb;
694 $table_name = $wpdb->prefix . 'project_timer';
695 $wpdb->query("TRUNCATE TABLE $table_name");
696 
697 update_option('project_timer_total_time', 0);
698 
699 wp_send_json_success();
700}

0

Related Snippets

Please see some snippets below related to this snippet..

General

AI Verified

0

FacetWP Proximity - limit to country

Added: 6 months ago

Last Updated: 1 month ago

General

AI Verified

0

Delete WP Automatically Generated Images (not tested and audited yet)

Added: 7 months ago

Last Updated: 7 months ago

Delete from the database the images that WP automatically generated from uploaded images

General

AI Verified

0

Allow [code_snippet] shortcode in excerpts

Added: 9 months ago

Last Updated: 9 months ago

By default, WordPress strips all shortcode tags out completely when creating the excerpt text for a post. This snippet prevents that from happening for the [code_snippet] shortcode, allowing you to em...

Other Snippets in this Codevault

These are some popular snippets from this users codevault..

General

AI Verified

19

Convert To WebP

Added: 7 months ago

Last Updated: 10 hours ago

<p>Snippet to convert JPG / PNG / Gif to WebP automatically on upload. Used GD or ImageMagick</p>

WordPress Admin

AI Verified

5

Really Simple Duplications

Added: 7 months ago

Last Updated: 4 months ago

A snippet to duplicate posts and pages and CPTS.

General

AI Verified

2

WP-Admin ChatGPT

Added: 4 months ago

Last Updated: 1 month ago