Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion public/plugin/BuyCourses/lang/en_US.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@
$strings['YouNeedToBeRegisteredInAtLeastOneSession'] = 'You need to be registered in at least one session';
$strings['IfYouWantToGetTheCertificateAndOrSkillsAsociatedToThisCourseYouNeedToBuyTheCertificateServiceYouCanGoToServiceCatalogClickingHere'] = "To obtain the certificate and/or the skills associated to this course, you need to buy the <b> Certificate </b> service. Go to the services catalogue to buy it by clicking <a target='_blank' href='%s'>here</a>";
$strings['ServiceDeleted'] = 'Service deleted';
$strings['ServiceCopied'] = 'Service copied successfully.';
$strings['ServiceCopyFailed'] = 'The service could not be copied.';
$strings['ServiceCopyInvalidSecurityToken'] = 'The copy request is no longer valid. Please try again.';
$strings['YourCoursesNeedAtLeastOneLearningPath'] = 'The courses to which you are subscribed need at least one learning path that contains a final certificate item';
$strings['GlobalTaxPerc'] = 'Global tax rate';
$strings['GlobalTaxPercDescription'] = 'Default tax rate that will be used unless there is a specific tax rate for the course, session or service.';
Expand Down Expand Up @@ -596,4 +599,4 @@
$strings['MaxMindAccountId'] = 'MaxMind account ID';
$strings['MaxMindAccountIdHelp'] = 'Required only when MaxMind web service is selected.';
$strings['MaxMindLicenseKey'] = 'MaxMind license key';
$strings['MaxMindLicenseKeyHelp'] = 'Required only when MaxMind web service is selected. Keep this value private.';
$strings['MaxMindLicenseKeyHelp'] = 'Required only when MaxMind web service is selected. Keep this value private.';
3 changes: 3 additions & 0 deletions public/plugin/BuyCourses/lang/es.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@
$strings['YouNeedToBeRegisteredInAtLeastOneSession'] = 'Necesitas estar registrado en al menos una sesión';
$strings['IfYouWantToGetTheCertificateAndOrSkillsAsociatedToThisCourseYouNeedToBuyTheCertificateServiceYouCanGoToServiceCatalogClickingHere'] = "Si quieres obtener el certificado y/o las competencias asociadas a este curso, necesitas comprar el servicio de <b> Certificado </b>, puedes ir al catálogo de servicios para comprarlo haciendo click <a target='_blank' href='%s'>AQUÍ</a>";
$strings['ServiceDeleted'] = 'Servicio eliminado';
$strings['ServiceCopied'] = 'Servicio copiado correctamente.';
$strings['ServiceCopyFailed'] = 'No se pudo copiar el servicio.';
$strings['ServiceCopyInvalidSecurityToken'] = 'La solicitud de copia ya no es válida. Inténtalo nuevamente.';
$strings['YourCoursesNeedAtLeastOneLearningPath'] = 'Los cursos en los que estás registrado necesitan tener al menos una lección que contenga un item de cerficado final';
$strings['GlobalTaxPerc'] = 'Porcentaje del impuesto global';
$strings['GlobalTaxPercDescription'] = 'Porcentaje por defecto que se usará, excepto si existe un impuesto específico en el curso, sesión o servicio.';
Expand Down
3 changes: 3 additions & 0 deletions public/plugin/BuyCourses/lang/fr_FR.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@
$strings['YouNeedToBeRegisteredInAtLeastOneSession'] = 'You need to be registered in at least one session';
$strings['IfYouWantToGetTheCertificateAndOrSkillsAsociatedToThisCourseYouNeedToBuyTheCertificateServiceYouCanGoToServiceCatalogClickingHere'] = "Pour obtenir le certificat et/ou les compétences associés à ce cours, vous devez acheter le service <b> Certificat </b>. Allez au catalogue des services pour l'acheter en cliquant <a target='_blank' href='%s'>ici</a>";
$strings['ServiceDeleted'] = 'Service deleted';
$strings['ServiceCopied'] = 'Service copié avec succès.';
$strings['ServiceCopyFailed'] = 'Le service n’a pas pu être copié.';
$strings['ServiceCopyInvalidSecurityToken'] = 'La demande de copie n’est plus valide. Veuillez réessayer.';
$strings['YourCoursesNeedAtLeastOneLearningPath'] = 'The courses to which you are subscribed need at least one learning path that contains a final certificate item';
$strings['GlobalTaxPerc'] = 'Global tax rate';
$strings['GlobalTaxPercDescription'] = 'Default tax rate that will be used unless there is a specific tax rate for the course, session or service.';
Expand Down
134 changes: 134 additions & 0 deletions public/plugin/BuyCourses/src/buy_course_plugin.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -3927,6 +3927,140 @@ public function saveServiceBenefitConfigurations(int $serviceId, array $serviceD
}
}

public function copyService(int $id)
{
if ($id <= 0) {
return false;
}

$servicesTable = Database::get_main_table(self::TABLE_SERVICES);
$service = Database::select(
'*',
$servicesTable,
[
'where' => ['id = ?' => $id],
],
'first'
);

if (empty($service) || !is_array($service)) {
return false;
}

$sourceName = trim((string) ($service['name'] ?? ''));
if ('' === $sourceName) {
$sourceName = $this->get_lang('Service');
}

$sourceImageName = (string) ($service['image'] ?? '');

$copiedServiceId = Database::insert(
$servicesTable,
[
'name' => $sourceName.' (copy)',
'description' => (string) ($service['description'] ?? ''),
'price' => isset($service['price']) ? (float) $service['price'] : 0.0,
'tax_perc' => '' !== (string) ($service['tax_perc'] ?? '') ? (int) $service['tax_perc'] : null,
'duration_days' => isset($service['duration_days']) ? (int) $service['duration_days'] : 0,
'renewable' => !empty($service['renewable']) ? 1 : 0,
'total_charges' => isset($service['total_charges']) ? (int) $service['total_charges'] : 0,
'allow_trial' => !empty($service['allow_trial']) ? 1 : 0,
'trial_period' => (string) ($service['trial_period'] ?? ''),
'trial_frequency' => isset($service['trial_frequency']) ? (int) $service['trial_frequency'] : 0,
'trial_total_charges' => isset($service['trial_total_charges']) ? (int) $service['trial_total_charges'] : 0,
'max_subscribers' => isset($service['max_subscribers']) ? (int) $service['max_subscribers'] : 0,
'subscription_behavior_json' => (string) ($service['subscription_behavior_json'] ?? ''),
'stripe_price_id' => (string) ($service['stripe_price_id'] ?? ''),
'display_on_course_creation_page' => !empty($service['display_on_course_creation_page']) ? 1 : 0,
'applies_to' => isset($service['applies_to']) ? (int) $service['applies_to'] : self::SERVICE_TYPE_NONE,
'owner_id' => isset($service['owner_id']) ? (int) $service['owner_id'] : api_get_user_id(),
'visibility' => !empty($service['visibility']) ? 1 : 0,
'image' => '',
'video_url' => (string) ($service['video_url'] ?? ''),
'service_information' => (string) ($service['service_information'] ?? ''),
]
);

if (!$copiedServiceId) {
return false;
}

$copiedImageName = 'simg-'.(int) $copiedServiceId.'.png';
if ($this->copyServiceImage($sourceImageName, $copiedImageName)) {
Database::update(
$servicesTable,
['image' => $copiedImageName],
['id = ?' => (int) $copiedServiceId]
);
}

$this->copyServiceBenefitConfigurations($id, (int) $copiedServiceId);

return (int) $copiedServiceId;
}

private function copyServiceBenefitConfigurations(int $sourceServiceId, int $targetServiceId): void
{
if ($sourceServiceId <= 0 || $targetServiceId <= 0) {
return;
}

if (!$this->hasPluginTable(self::TABLE_SERVICE_REL_EXTRA_FIELD)) {
return;
}

$serviceRelTable = Database::get_main_table(self::TABLE_SERVICE_REL_EXTRA_FIELD);
$rows = Database::select(
'*',
$serviceRelTable,
[
'where' => ['service_id = ?' => $sourceServiceId],
]
);

foreach ($rows as $row) {
$extraFieldId = isset($row['extra_field_id']) ? (int) $row['extra_field_id'] : 0;
$grantedValue = isset($row['granted_value']) ? (int) $row['granted_value'] : 0;

if ($extraFieldId <= 0 || $grantedValue <= 0) {
continue;
}

Database::insert($serviceRelTable, [
'service_id' => $targetServiceId,
'extra_field_id' => $extraFieldId,
'granted_value' => $grantedValue,
]);
}
}

private function copyServiceImage(string $sourceImageName, string $targetImageName): bool
{
if ('' === $sourceImageName || '' === $targetImageName) {
return false;
}

if (!$this->serviceImageExists($sourceImageName)) {
return false;
}

try {
$pluginsFilesystem = Container::getPluginsFileSystem();
$directory = $this->getServiceImagesDirectory();

if (!$pluginsFilesystem->directoryExists($directory)) {
$pluginsFilesystem->createDirectory($directory);
}

$content = $pluginsFilesystem->read($this->getServiceImageStoragePath($sourceImageName));
$pluginsFilesystem->write($this->getServiceImageStoragePath($targetImageName), $content);
} catch (\Throwable) {
return false;
}

return true;
}

/**
* update a service.
*
Expand Down
40 changes: 40 additions & 0 deletions public/plugin/BuyCourses/src/list_service.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
$plugin = BuyCoursesPlugin::create();
$httpRequest = Container::getRequest();

$copyServiceTokenKey = 'buycourses_copy_service_token';
if (empty($_SESSION[$copyServiceTokenKey])) {
$_SESSION[$copyServiceTokenKey] = bin2hex(random_bytes(32));
}

$includeSession = 'true' === $plugin->get('include_sessions');
$includeServices = 'true' === $plugin->get('include_services');

Expand All @@ -27,6 +32,40 @@

api_protect_admin_script(true);

if ('POST' === $httpRequest->getMethod() && 'copy_service' === $httpRequest->request->get('action')) {
$serviceId = $httpRequest->request->getInt('id');
$postedToken = (string) $httpRequest->request->get('copy_service_token', '');
$sessionToken = (string) ($_SESSION[$copyServiceTokenKey] ?? '');

if ('' === $sessionToken || !hash_equals($sessionToken, $postedToken)) {
Display::addFlash(
Display::return_message($plugin->get_lang('ServiceCopyInvalidSecurityToken'), 'warning')
);

header('Location: list_service.php');
exit;
}

$_SESSION[$copyServiceTokenKey] = bin2hex(random_bytes(32));

$copiedServiceId = $plugin->copyService($serviceId);
if (false === $copiedServiceId) {
Display::addFlash(
Display::return_message($plugin->get_lang('ServiceCopyFailed'), 'error')
);

header('Location: list_service.php');
exit;
}

Display::addFlash(
Display::return_message($plugin->get_lang('ServiceCopied'), 'success')
);

header('Location: services_edit.php?id='.(int) $copiedServiceId);
exit;
}

$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE;
$currentPage = max(1, $httpRequest->query->getInt('page', 1));
$first = $pageSize * ($currentPage - 1);
Expand Down Expand Up @@ -72,6 +111,7 @@
$tpl->assign('courses', []);
$tpl->assign('sessions', []);
$tpl->assign('services', $services);
$tpl->assign('copy_service_token', (string) ($_SESSION[$copyServiceTokenKey] ?? ''));

$tpl->assign('course_pagination', '');
$tpl->assign('session_pagination', '');
Expand Down
33 changes: 26 additions & 7 deletions public/plugin/BuyCourses/view/list.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -603,13 +603,32 @@
{% endif %}

<td class="px-6 py-4 text-right">
<a
href="{{ url('index') ~ 'plugin/BuyCourses/src/services_edit.php?' ~ {'id': item.id}|url_encode }}"
class="inline-flex items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2"
>
<em class="fa fa-wrench fa-fw"></em>
{{ 'Edit'|get_lang }}
</a>
<div class="inline-flex flex-wrap items-center justify-end gap-2">
<a
href="{{ url('index') ~ 'plugin/BuyCourses/src/services_edit.php?' ~ {'id': item.id}|url_encode }}"
class="inline-flex items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2"
>
<em class="fa fa-wrench fa-fw"></em>
{{ 'Edit'|get_lang }}
</a>

<form
method="post"
action="{{ url('index') ~ 'plugin/BuyCourses/src/list_service.php' }}"
class="inline-flex"
>
<input type="hidden" name="action" value="copy_service">
<input type="hidden" name="id" value="{{ item.id }}">
<input type="hidden" name="copy_service_token" value="{{ copy_service_token }}">
<button
type="submit"
class="inline-flex items-center justify-center gap-2 rounded-xl border border-gray-25 bg-white px-4 py-2.5 text-sm font-semibold text-gray-90 shadow-sm transition hover:border-primary/30 hover:text-primary focus:outline-none focus:ring-2 focus:ring-primary/20 focus:ring-offset-2"
>
<em class="fa fa-copy fa-fw"></em>
{{ 'Copy'|get_lang }}
</button>
</form>
</div>
</td>
</tr>
{% else %}
Expand Down
Loading