[TikiWiki-commits] [Git][tikiwiki/tiki][master] [FIX][ENH][UX] Calendar: 'Copy to new event' workflow
"Victor Emanouilov \(@kroky\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <69aeeee218b38_3b1892f04585c@gitlab-sidekiq-low-urgency-cpu-bound-v2-74685b5685-vxj9q.mail> |
Victor Emanouilov pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
08bd0c0a by MAGENE Sem Joel at 2026-03-09T15:21:48+00:00
[FIX][ENH][UX] Calendar: 'Copy to new event' workflow
---
* [FIX][ENH][UX] Calendar: 'Copy to new event' workflow
See merge request tikiwiki/tiki!9667
- - - - -
6 changed files:
- doc/devtools/codesniffer/standards/TikiIgnore/ignore_list.json
- lib/calendar/calendarlib.php
- lib/core/Services/Calendar/Controller.php
- templates/calendar/edit_item.tpl
- templates/calendar/view_item.tpl
- themes/base_files/scss/_tiki-bootstrap_overrides.scss
Changes:
=====================================
doc/devtools/codesniffer/standards/TikiIgnore/ignore_list.json
=====================================
@@ -5580,7 +5580,6 @@
"Services_Calendar_Controller::action_del_me": true,
"Services_Calendar_Controller::action_list_items": true,
"Services_Calendar_Controller::action_edit_item": true,
- "Services_Calendar_Controller::action_copy_item": true,
"Services_Calendar_Controller::action_view_item": true,
"Services_Calendar_Controller::action_delete_item": true,
"Services_Calendar_Controller::action_delete_recurrent_items": true
=====================================
lib/calendar/calendarlib.php
=====================================
@@ -2385,4 +2385,59 @@ class CalendarLib extends TikiLib
}
return count(array_unique($dates));
}
+
+ /**
+ * Prepares an event array for cloning/copying.
+ * Removes unique identifiers, resets status, and sets the new owner.
+ *
+ * @param int $sourceCalItemId The ID of the event to copy
+ * @param string $user The current user (new owner)
+ * @return array|null Returns the cleaned event array or null if not found
+ */
+ public function getCopyData($sourceCalItemId, $user)
+ {
+ $item = $this->get_item($sourceCalItemId);
+
+ if (! $item) {
+ return null;
+ }
+
+ $item['calitemId'] = 0;
+ $item['recurrenceId'] = 0;
+ $item['uid'] = ''; // Will be regenerated on save
+ $item['uri'] = '';
+ $item['created'] = 0;
+ $item['lastModif'] = 0;
+
+ $item['user'] = $user;
+ $item['organizers'] = [$user];
+ $item['name'] = tr('Copy of %0', $item['name']);
+ $userInParticipants = false;
+
+ if (! empty($item['participants'])) {
+ foreach ($item['participants'] as &$participant) {
+ $participant['partstat'] = '';
+ $participant['comment'] = '';
+ if ($participant['username'] === $user) {
+ $userInParticipants = true;
+ }
+ }
+ } else {
+ $item['participants'] = [];
+ }
+ if (! $userInParticipants) {
+ $item['participants'][] = [
+ 'username' => $user,
+ 'role' => '',
+ 'partstat' => '',
+ 'comment' => ''
+ ];
+ if (isset($item['selected_participants'])) {
+ $item['selected_participants'][] = $user;
+ } else {
+ $item['selected_participants'] = [$user];
+ }
+ }
+ return $item;
+ }
}
=====================================
lib/core/Services/Calendar/Controller.php
=====================================
@@ -304,8 +304,8 @@ class Services_Calendar_Controller extends Services_Calendar_BaseController
if ($return_url && ! $access->is_xml_http_request()) {
$access->redirect($return_url, tr('The event was saved successfully'));
}
- // reload the page?
- return [];
+ // Ensure AJAX modals redirects
+ return ['url' => $return_url ?: 'tiki-calendar.php'];
}
} else {
Feedback::error(tr('Calendar edit error')); // TODO more
@@ -368,98 +368,127 @@ class Services_Calendar_Controller extends Services_Calendar_BaseController
$trackerItems = $this->calendarLib->getAttachedTrackerItems($calitemId);
} else {
- // new event
- $title = tr('Calendar event : %0', tr('New'));
+ // new event or copy action
$calitemId = 0;
- $calendar = $calendars[0];
- $calendarId = $input->defaultCalendarId->int() > 0 ? $input->defaultCalendarId->int() : $calendar['calendarId'];
-
- $participants = [];
- if ($user) {
- $participants[] = [
- 'username' => $user,
- 'role' => '',
- 'partstat' => '',
- ];
+ $calitem = null; // Initialize to ensure clean state
+ $trackerItems = [];
+ if ($input->copy_from->int()) {
+ $copyFromId = $input->copy_from->int();
+ $input->offsetSet('calitemId', $copyFromId);
+ $copyFromId = $this->getItemId($input, 'view_events'); // Throws exception if unauthorized
+ $input->offsetSet('calitemId', 0); // Reset back to 0 for new event creation
+ $calitem = $this->calendarLib->getCopyData($copyFromId, $user);
+ if ($calitem) {
+ $title = tr('Calendar event : %0', $calitem['name']);
+ $calendarId = $calitem['calendarId'];
+ $calendar = $this->calendarLib->get_calendar($calendarId);
+ $trackerItems = $this->calendarLib->getAttachedTrackerItems($copyFromId);
+ // Apply timezone offset for the UI form
+ $start = new TikiDate();
+ $start->setDate($calitem['start']);
+ $start->setTZbyID($displayTimezone);
+ $end = new TikiDate();
+ $end->setDate($calitem['end']);
+ $end->setTZbyID($displayTimezone);
+
+ $calitem['start'] = $start->getTime();
+ $calitem['end'] = $end->getTime();
+ $calitem['duration'] = 0;
+ }
}
- // set up default start and end
- $dateNow->setTZbyID($displayTimezone);
- if ($input->prefill_start->text()) {
- $prefillStart = $input->prefill_start->text();
- $prefillEnd = $input->prefill_end->text();
-
- $tikidate = new TikiDate();
- $tikidate->setTZbyID($displayTimezone);
-
- $tikidate->setDate($prefillStart, $displayTimezone);
- $start = $tikidate->getTime();
- if ($prefillEnd && strtotime($prefillEnd) !== false) {
- $tikidate->setDate($prefillEnd, $displayTimezone);
- $end = $tikidate->getTime();
- // subtract 1 sec to make it inclusive
- if (strlen($prefillEnd) <= 10 || strpos($prefillEnd, '00:00:00') !== false) {
- $end -= 1;
- }
- $duration = $end - $start;
- } else {
- $duration = 60 * 60;
- $end = $start + $duration;
- }
- if ($input->target_user->text()) {
- if ($user) {
- $participants[0]['role'] = '1';
- $participants[0]['partstat'] = 'ACCEPTED';
- }
+ if (empty($calitem)) {
+ $title = tr('Calendar event : %0', tr('New'));
+ $calendar = $calendars[0];
+ $calendarId = $input->defaultCalendarId->int() > 0 ? $input->defaultCalendarId->int() : $calendar['calendarId'];
+
+ $participants = [];
+ if ($user) {
$participants[] = [
- 'username' => $input->target_user->text(),
- 'role' => '1',
+ 'username' => $user,
+ 'role' => '',
'partstat' => '',
];
}
- } else {
- $hour = $dateNow->date->format('H');
- if ($input->offsetExists('todate')) {
- $dateNow->setTZbyID($displayTimezone);
- $dateNow->setDate($input->todate->text(), $displayTimezone);
+ // set up default start and end
+ $dateNow->setTZbyID($displayTimezone);
+ if ($input->prefill_start->text()) {
+ $prefillStart = $input->prefill_start->text();
+ $prefillEnd = $input->prefill_end->text();
+
+ $tikidate = new TikiDate();
+ $tikidate->setTZbyID($displayTimezone);
+
+ $tikidate->setDate($prefillStart, $displayTimezone);
+ $start = $tikidate->getTime();
+ if ($prefillEnd && strtotime($prefillEnd) !== false) {
+ $tikidate->setDate($prefillEnd, $displayTimezone);
+ $end = $tikidate->getTime();
+ // subtract 1 sec to make it inclusive
+ if (strlen($prefillEnd) <= 10 || strpos($prefillEnd, '00:00:00') !== false) {
+ $end -= 1;
+ }
+ $duration = $end - $start;
+ } else {
+ $duration = 60 * 60;
+ $end = $start + $duration;
+ }
+ if ($input->target_user->text()) {
+ if ($user) {
+ $participants[0]['role'] = '1';
+ $participants[0]['partstat'] = 'ACCEPTED';
+ }
+ $participants[] = [
+ 'username' => $input->target_user->text(),
+ 'role' => '1',
+ 'partstat' => '',
+ ];
+ }
+ } else {
$hour = $dateNow->date->format('H');
+ if ($input->offsetExists('todate')) {
+ // set the correct day clicked on
+ $dateNow->setTZbyID($displayTimezone);
+ $dateNow->setDate($input->todate->text(), $displayTimezone);
+ $hour = $dateNow->date->format('H');
+ }
+ $tz = date_default_timezone_get();
+ date_default_timezone_set($displayTimezone);
+ $start = mktime(
+ $hour,
+ $dateNow->date->format('i'),
+ $dateNow->date->format('s'),
+ $dateNow->date->format('m'),
+ $dateNow->date->format('d'),
+ $dateNow->date->format('Y')
+ );
+ date_default_timezone_set($tz);
+ $duration = 60 * 60;
+ $end = $start + $duration;
}
- $tz = date_default_timezone_get();
- date_default_timezone_set($displayTimezone);
- $start = mktime(
- $hour,
- $dateNow->date->format('i'),
- $dateNow->date->format('s'),
- $dateNow->date->format('m'),
- $dateNow->date->format('d'),
- $dateNow->date->format('Y')
- );
- date_default_timezone_set($tz);
- $duration = 60 * 60;
- $end = $start + $duration;
- }
- $calitem = [
- 'calitemId' => $calitemId,
- 'calendarId' => $calendarId,
- 'user' => $user,
- 'name' => $input->prefill_title->text(),
- 'url' => '',
- 'description' => '',
- 'status' => $calendar['defaulteventstatus'],
- 'priority' => 0,
- 'locationId' => 0,
- 'categoryId' => 0,
- 'nlId' => 0,
- 'start' => $start,
- 'end' => $end,
- 'duration' => $duration,
- 'recurrenceId' => 0,
- 'allday' => $calendar['allday'] == 'y' ? 1 : 0,
- 'organizers' => [$user],
- 'participants' => $participants,
- 'returnURL' => $return_url,
- ];
+ $calitem = [
+ 'calitemId' => $calitemId,
+ 'calendarId' => $calendarId,
+ 'user' => $user,
+ 'name' => $input->prefill_title->text(),
+ 'url' => '',
+ 'description' => '',
+ 'status' => $calendar['defaulteventstatus'],
+ 'priority' => 0,
+ 'locationId' => 0,
+ 'categoryId' => 0,
+ 'nlId' => 0,
+ 'start' => $start,
+ 'end' => $end,
+ 'duration' => $duration,
+ 'recurrenceId' => 0,
+ 'allday' => $calendar['allday'] == 'y' ? 1 : 0,
+ 'organizers' => [$user],
+ 'participants' => $participants,
+ 'returnURL' => $return_url,
+ ];
+ }
}
if (isset($calitem['recurrenceId']) && $calitem['recurrenceId'] > 0) {
@@ -598,13 +627,6 @@ class Services_Calendar_Controller extends Services_Calendar_BaseController
];
}
- public function action_copy_item(JitFilter $input): array
- {
- $input->offsetSet('calitemId', 0);
-
- return $this->action_edit_item($input);
- }
-
/**
* Shows a calendar item (event)
*
=====================================
templates/calendar/edit_item.tpl
=====================================
@@ -443,10 +443,6 @@
<input type="hidden" id="act" name="act" value="">
<input type="submit" class="btn btn-secondary cleanable-false" name="preview" value="{tr}Preview{/tr}" onclick="needToConfirm=false">
<input type="submit" class="btn btn-primary cleanable-false {if $prefilled}need-participant{/if}" name="saveitem" value="{tr}Save{/tr}" onclick="needToConfirm=false">
- {if $tiki_p_add_events eq 'y' and empty($saveas) and not empty($calitemId)}
- <input type="submit" class="btn btn-secondary cleanable-false" name="saveas" data-alt_controller="calendar" data-alt_action="copy_item"
- onclick="needToConfirm=false" value="{tr}Copy to a new event{/tr}">
- {/if}
{if $calitemId && ! $recurrence.id}
<input type="submit" name="delete" data-alt_controller="calendar" data-alt_action="delete_item"
class="btn btn-danger cleanable-false" onclick="needToConfirm=false;" data-bs-dismiss="modal" value="{tr}Delete event{/tr}">
=====================================
templates/calendar/view_item.tpl
=====================================
@@ -356,5 +356,13 @@
{tr}Edit{/tr}
</a>
{/permission}
+ {if $tiki_p_add_events eq 'y'}
+ <a
+ href="{service controller='calendar' action='edit_item' calitemId=0 copy_from=$calitem.calitemId|escape modal=1 return_url='tiki-calendar.php'}"
+ class="btn btn-tinted-primary edit-calendar-item-btn cleanable-false"
+ title="{tr}Create a new event based on this one{/tr}">
+ {icon name='copy'} {tr}Copy{/tr}
+ </a>
+ {/if}
{/if}
{/block}
=====================================
themes/base_files/scss/_tiki-bootstrap_overrides.scss
=====================================
@@ -458,6 +458,29 @@ body.fullscreen.tiki.navbar-padding {
margin-bottom: 0;
}
+.btn-tinted-primary {
+ --bs-btn-color: var(--bs-primary);
+ --bs-btn-bg: rgba(var(--bs-primary-rgb), 0.08);
+ --bs-btn-border-color: transparent;
+
+ --bs-btn-hover-color: var(--bs-primary);
+ --bs-btn-hover-bg: rgba(var(--bs-primary-rgb), 0.15);
+ --bs-btn-hover-border-color: transparent;
+
+ --bs-btn-active-color: var(--bs-primary);
+ --bs-btn-active-bg: rgba(var(--bs-primary-rgb), 0.20);
+ --bs-btn-active-border-color: transparent;
+
+ --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
+
+ color: var(--bs-btn-color) !important;
+
+ &:focus,
+ &:active {
+ box-shadow: none !important;
+ }
+}
+
@media screen and (max-width: 480px) {
.tiki #conversejs.converse-embedded {
margin: 0;
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/08bd0c0ad7937441573853df24b910d81f517889
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/08bd0c0ad7937441573853df24b910d81f517889
You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs