From 9b4c33c3913c75ee424262a077c4bc2e445381a9 Mon Sep 17 00:00:00 2001 From: praneettekdi Date: Fri, 15 Jan 2021 16:09:39 +0530 Subject: [PATCH] Merge Release 1.0.3 into Master (#87) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Task #165254 chore: Native Add to linkedin Button - Linkdin Education Profile certificate upload/share (#69) * Bug #164674 fix: search box for cert-xxxx is showing error (#48) * Bug #164625 fix: title is showing blank data, it should display course name (#49) * Bug #164705 fix: click on the social media post the back button on the certificate is of no use (#52) * Bug #164705 fix: click on the social media post :: the back button on the certificate is of no use * Bug #164705 fix: Resolve comment * Bug #164707 fix: user able to download others certificate as well (#53) * Bug #164707 fix: user able to download others certificate as well * Bug #164707 fix: Resolve comments * Bug #164707 fix: Resolve comments * Bug #164707 fix: Resolve comment * Task #164910 chore: Button to copy shareble certificate link (#51) * Task #164910 chore: Button to copy shareble certificate link * Task #164910 chore: Button to copy shareble certificate link * Task #164910 chore: Button to copy shareble certificate link * Task #164910 chore: Resolve comment * Task #164910 chore: Resolve comments * Task #164910 chore: Resolve comments * Task #164910 chore: Resolve comments * Task #164910 chore: Resolve comments * Task #163318 fix: Fixed image cut issue while image generation (#54) * Issue #164867 style: desing changes update (#50) Co-authored-by: “Mangesh <“mangesh_m@tekditechonologies.com”> * Task #163318 fix: Added fixed width height for certificate container (#58) * Bug165031 : Backend > Attendees > Error '0 Call to a member function getDownloadUrl() on string' occurs on checkin the attendee (#62) Co-authored-by: Snehal Patil * Task #165230 Chore: Back end>>Issued Certificate >> Search should also be done by username. (#59) * Task #165122 Chore: Backend certificate view changes- Adding Course co… (#55) * Task #165122 Chore: Backend certificate view changes- Adding Curse column at Issued certificate View * Changing title of column * Resolving conflicts * Task #165122 Chore: Backend Issued certificate view changes- Adding attendee… (#56) * Task #165122 Chore: Backend certificate view changes- Adding attendee name to the user column * Changing title of column User * Resolving phpcs issue * Resolving conflicts * Task #165166 Chore: Add Certificate Url column under Issued Certificate View (#57) * Task #165166 Chore: Add Copy Url column under Issued Cetificate VIew * Resolving conflicts * Resolving comments * Resolving comments * Resolving comments * Task #165166 Feat: Adding (#63) * Bug165031 : Backend > Attendees > Error '0 Call to a member function … (#64) * Bug165031 : Backend > Attendees > Error '0 Call to a member function … * Bug165031 : Backend > Attendees > Error '0 Call to a member function … Co-authored-by: Snehal Patil * Task #165259 Chore: Adding proper names to the client filter and column in Issue… (#66) * Task #165259 Chore: Adding proper names to the client filter in Issued Certificate view * Adding names to client column * Task #165259 Chore: Renaming Unique Certificate Id column name to Certificate Id at both end (#67) * Task #165259 Chore: Reordering columns of Issues Certificate View (#68) * Task #165254 chore: Native Add to linkedin Button - Linkdin Education Profile certificate upload/share * Task #165254 chore: Native Add to linkedin Button - Linkdin Education Profile certificate upload/share * Task #165254 chore: Resolve comments * Task #165254 chore: update download permission code * Task #165254 chore: Resolve comments * Task #165254 chore: Remove images Co-authored-by: praneettekdi Co-authored-by: Mangesh Mane Co-authored-by: “Mangesh <“mangesh_m@tekditechonologies.com”> Co-authored-by: snehal patil Co-authored-by: Snehal Patil Co-authored-by: divyachaudhari <58217051+divyachaudhari@users.noreply.github.com> * Bug #164674 fix: search box for cert-xxxx is showing error (#73) * Bug #164674 fix: search box for cert-xxxx is showing error * Bug #164674 fix: Resolve comments * Bug #164674 fix: Resolve commenys * Task #165166 Chore: Adding SEF url to the certificate copy Url functi… (#72) * Bug #164674 fix: search box for cert-xxxx is showing error (#48) * Bug #164625 fix: title is showing blank data, it should display course name (#49) * Bug #164705 fix: click on the social media post the back button on the certificate is of no use (#52) * Bug #164705 fix: click on the social media post :: the back button on the certificate is of no use * Bug #164705 fix: Resolve comment * Bug #164707 fix: user able to download others certificate as well (#53) * Bug #164707 fix: user able to download others certificate as well * Bug #164707 fix: Resolve comments * Bug #164707 fix: Resolve comments * Bug #164707 fix: Resolve comment * Task #164910 chore: Button to copy shareble certificate link (#51) * Task #164910 chore: Button to copy shareble certificate link * Task #164910 chore: Button to copy shareble certificate link * Task #164910 chore: Button to copy shareble certificate link * Task #164910 chore: Resolve comment * Task #164910 chore: Resolve comments * Task #164910 chore: Resolve comments * Task #164910 chore: Resolve comments * Task #164910 chore: Resolve comments * Task #163318 fix: Fixed image cut issue while image generation (#54) * Issue #164867 style: desing changes update (#50) Co-authored-by: “Mangesh <“mangesh_m@tekditechonologies.com”> * Task #163318 fix: Added fixed width height for certificate container (#58) * Bug165031 : Backend > Attendees > Error '0 Call to a member function getDownloadUrl() on string' occurs on checkin the attendee (#62) Co-authored-by: Snehal Patil * Task #165230 Chore: Back end>>Issued Certificate >> Search should also be done by username. (#59) * Task #165122 Chore: Backend certificate view changes- Adding Course co… (#55) * Task #165122 Chore: Backend certificate view changes- Adding Curse column at Issued certificate View * Changing title of column * Resolving conflicts * Task #165122 Chore: Backend Issued certificate view changes- Adding attendee… (#56) * Task #165122 Chore: Backend certificate view changes- Adding attendee name to the user column * Changing title of column User * Resolving phpcs issue * Resolving conflicts * Task #165166 Chore: Add Certificate Url column under Issued Certificate View (#57) * Task #165166 Chore: Add Copy Url column under Issued Cetificate VIew * Resolving conflicts * Resolving comments * Resolving comments * Resolving comments * Task #165166 Feat: Adding (#63) * Bug165031 : Backend > Attendees > Error '0 Call to a member function … (#64) * Bug165031 : Backend > Attendees > Error '0 Call to a member function … * Bug165031 : Backend > Attendees > Error '0 Call to a member function … Co-authored-by: Snehal Patil * Task #165259 Chore: Adding proper names to the client filter and column in Issue… (#66) * Task #165259 Chore: Adding proper names to the client filter in Issued Certificate view * Adding names to client column * Task #165259 Chore: Renaming Unique Certificate Id column name to Certificate Id at both end (#67) * Task #165166 Chore: Adding SEF url to the certificate copy Url functioning * Changing the routing of getUrl() * Changing the routing of getUrl() * Changing the routing of getUrl() Co-authored-by: Tushar Shekokar Co-authored-by: praneettekdi Co-authored-by: Mangesh Mane Co-authored-by: “Mangesh <“mangesh_m@tekditechonologies.com”> Co-authored-by: snehal patil Co-authored-by: Snehal Patil * Task #165166 Chore: Adding SEF url to the certificate copy Url functioning (#74) * Bug #163318 fix: Fixed image generation issue in smaller devices (#75) * Issue #166260 fix: Render default template issue when a different editor is used other than tinyMCE & CodeMirror (#78) * Merge Release 1.0.4 into Release 1.0.3 (#79) * Task #165032 chore: Changes in TjCertificate Issued Certificate list view to allow admin to edit Issued Certificates (#76) * Task #165032 chore: Changes in TjCertificate Issued Certificate list view to allow admin to edit Issued Certificate * Task #165032 chore: Changes in TjCertificate Issued Certificate list view to allow admin to edit Issued Certificates * Task #165032 chore: Backend list view changes * Task #165032 chore: Resolve comments * Task #165032 chore: Resolve comments * Task #165032 chore: Resolve comments * Task #165032 chore: Resolve comments * Feature #165030 chore: Changes for certificate re-generate feature (#77) * Feature #165030 chore: Changes for certificate re-generate feature * Feature #165030 chore: Resolve comments * Task #166208 chore: Tj certificate list view changes * Feature #165030 chore: Delete certificate image * Feature #165030 chore: Changes for certificate re-generate feature * Update view.html.php Co-authored-by: Tushar Shekokar * Task #165032 chore: Resolve conflicts and update folder in xml (#80) * Task #165032 chore: Changes in TjCertificate Issued Certificate list view to allow admin to edit Issued Certificates (#76) * Task #165032 chore: Changes in TjCertificate Issued Certificate list view to allow admin to edit Issued Certificate * Task #165032 chore: Changes in TjCertificate Issued Certificate list view to allow admin to edit Issued Certificates * Task #165032 chore: Backend list view changes * Task #165032 chore: Resolve comments * Task #165032 chore: Resolve comments * Task #165032 chore: Resolve comments * Task #165032 chore: Resolve comments * Feature #165030 chore: Changes for certificate re-generate feature (#77) * Feature #165030 chore: Changes for certificate re-generate feature * Feature #165030 chore: Resolve comments * Task #166208 chore: Tj certificate list view changes * Feature #165030 chore: Delete certificate image * Feature #165030 chore: Changes for certificate re-generate feature * Update view.html.php * Task #165032 chore: Resolve conflicts * Task #165032 chore: Add version in XML * Task #165032 chore: Resolve comments * Task #165032 chore: Resolve scrutinizer issue Co-authored-by: praneettekdi * Issue #166434 task: Server side certificate image generation using Imagick (#81) * Issue #166434 task: Server side certificate image generation using Imagick * Issue #166434 task: Server side certificate image generation using Imagick * Issue #166434 task: Server side certificate image generation using Imagick * Issue #166434 task: Server side certificate image generation using Imagick * Issue #166434 task: Server side certificate image generation using Imagick * Issue #166434 task: Server side certificate image generation using Imagick * Issue #166434 task: Server side certificate image generation using Imagick * Update default.php Make sure Canvas image generation works even if the global config values are not saved * Feature #166428 chore: Add external record and integrate tjnotification (#82) * Feature #166428 chore: Add external record and integrate tjnotification * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Update file and class name * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Backend functionality for add training records * Feature #166428 chore: Backend functionality for add training records * Feature #166428 chore: Backend functionality for add training records * Feature #166428 chore: Backend functionality for add training records * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Update sql file version * Feature #166428 chore: Update js * Feature #166428 chore: Update js * Feature #166428 chore: Update changes after UT (#83) * Feature #166428 chore: Add external record and integrate tjnotification * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Update file and class name * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Backend functionality for add training records * Feature #166428 chore: Backend functionality for add training records * Feature #166428 chore: Backend functionality for add training records * Feature #166428 chore: Backend functionality for add training records * Feature #166428 chore: Resolve comments * Feature #166428 chore: Resolve comments * Feature #166428 chore: Update sql file version * Feature #166428 chore: Update js * Feature #166428 chore: Update js * Feature #166428 chore: Update changes after UT * Feature #166428 chore: Load fontawesome css on init * Feature #166428 chore :Fix Bug #167401 and Bug #167402 (#84) * Bug #167400 fix: Redirect on list view on after action 'Save & Close' in backend (#85) * Feature #166428 chore :Fix Bug #167401 and Bug #167402 * Bug #167400 fix: Redirect on list view on after action 'Save & Close' in backend * Bug #167400 fix: Resolve comment Co-authored-by: Tushar Shekokar Co-authored-by: Mangesh Mane Co-authored-by: “Mangesh <“mangesh_m@tekditechonologies.com”> Co-authored-by: snehal patil Co-authored-by: Snehal Patil Co-authored-by: divyachaudhari <58217051+divyachaudhari@users.noreply.github.com> --- .../administrator/access.xml | 3 + .../administrator/config.xml | 39 ++- .../administrator/controllers/certificate.php | 104 ++++++- .../controllers/certificates.php | 45 +++ .../controllers/template.json.php | 32 ++ .../controllers/trainingrecord.json.php | 154 ++++++++++ .../controllers/trainingrecord.php | 242 +++++++++++++++ .../administrator/includes/tjcertificate.php | 48 +++ .../en-GB/en-GB.com_tjcertificate.ini | 96 +++++- .../en-GB/en-GB.com_tjcertificate.sys.ini | 3 + .../administrator/layouts/preview.php | 39 +++ .../administrator/libraries/certificate.php | 262 ++++++++++++++-- .../administrator/libraries/language.php | 38 +++ .../administrator/libraries/mails.php | 175 +++++++++++ .../administrator/models/certificate.php | 131 ++++++++ .../administrator/models/certificates.php | 7 +- .../models/fields/certificatetemplates.php | 11 +- .../models/forms/certificate.xml | 8 +- .../models/forms/filter_certificates.xml | 15 +- .../models/forms/filter_templates.xml | 9 +- .../models/forms/trainingrecord.xml | 131 ++++++++ .../administrator/models/trainingrecord.php | 290 ++++++++++++++++++ .../administrator/sql/install.mysql.utf8.sql | 42 +++ .../administrator/sql/updates/mysql/1.0.3.sql | 42 +++ .../administrator/tjcertificate.php | 1 + .../administrator/tjcertificateTemplate.json | 158 ++++++++++ .../views/certificate/tmpl/edit.php | 43 ++- .../views/certificate/view.html.php | 29 +- .../views/certificates/tmpl/default.php | 76 +++-- .../views/certificates/view.html.php | 8 +- .../views/template/tmpl/edit.php | 32 +- .../views/templates/tmpl/default.php | 10 +- .../views/trainingrecord/index.html | 0 .../views/trainingrecord/tmpl/edit.php | 111 +++++++ .../views/trainingrecord/tmpl/index.html | 0 .../views/trainingrecord/tmpl/preview.php | 95 ++++++ .../views/trainingrecord/view.html.php | 182 +++++++++++ .../media/images/buttons/en_US.png | Bin 0 -> 139290 bytes .../com_tjcertificate/media/js/certificate.js | 165 ++++++++++ .../media/js/certificate.min.js | 1 + .../media/js/certificateImage.js | 14 + .../media/js/certificateImage.min.js | 2 +- .../com_tjcertificate/media/js/template.js | 60 ++-- .../media/js/template.min.js | 2 +- .../media/js/tjCertificateService.js | 22 +- .../media/js/tjCertificateService.min.js | 2 +- .../script.tjcertificate.php | 33 ++ .../site/controllers/certificates.php | 106 +++++++ .../site/controllers/trainingrecord.json.php | 15 + .../site/controllers/trainingrecord.php | 14 + .../com_tjcertificate/site/events/record.php | 89 ++++++ .../en-GB/en-GB.com_tjcertificate.ini | 53 ++++ .../site/models/fields/createdby.php | 57 ++++ .../site/models/fields/getclientlist.php | 17 +- .../site/models/fields/users.php | 73 +++++ .../site/models/forms/filter_certificates.xml | 24 ++ .../site/models/forms/trainingrecord.xml | 136 ++++++++ .../site/models/trainingrecord.php | 14 + .../com_tjcertificate/site/tjcertificate.php | 1 + .../site/views/certificate/tmpl/default.php | 44 +-- .../tmpl/default_social_sharing.php | 7 + .../site/views/certificate/view.html.php | 59 ++-- .../site/views/certificates/tmpl/my.php | 108 ++++++- .../site/views/certificates/view.html.php | 28 +- .../site/views/trainingrecord/index.html | 0 .../views/trainingrecord/tmpl/default.php | 102 ++++++ .../site/views/trainingrecord/tmpl/edit.php | 117 +++++++ .../site/views/trainingrecord/tmpl/edit.xml | 8 + .../site/views/trainingrecord/view.html.php | 93 ++++++ .../com_tjcertificate/tjcertificate.xml | 8 +- 70 files changed, 3993 insertions(+), 192 deletions(-) create mode 100644 src/components/com_tjcertificate/administrator/controllers/trainingrecord.json.php create mode 100644 src/components/com_tjcertificate/administrator/controllers/trainingrecord.php create mode 100644 src/components/com_tjcertificate/administrator/layouts/preview.php create mode 100644 src/components/com_tjcertificate/administrator/libraries/language.php create mode 100644 src/components/com_tjcertificate/administrator/libraries/mails.php create mode 100644 src/components/com_tjcertificate/administrator/models/forms/trainingrecord.xml create mode 100644 src/components/com_tjcertificate/administrator/models/trainingrecord.php create mode 100644 src/components/com_tjcertificate/administrator/sql/updates/mysql/1.0.3.sql create mode 100644 src/components/com_tjcertificate/administrator/tjcertificateTemplate.json create mode 100644 src/components/com_tjcertificate/administrator/views/trainingrecord/index.html create mode 100644 src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/edit.php create mode 100644 src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/index.html create mode 100644 src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/preview.php create mode 100644 src/components/com_tjcertificate/administrator/views/trainingrecord/view.html.php create mode 100644 src/components/com_tjcertificate/media/images/buttons/en_US.png create mode 100644 src/components/com_tjcertificate/media/js/certificate.js create mode 100644 src/components/com_tjcertificate/media/js/certificate.min.js create mode 100644 src/components/com_tjcertificate/site/controllers/certificates.php create mode 100644 src/components/com_tjcertificate/site/controllers/trainingrecord.json.php create mode 100644 src/components/com_tjcertificate/site/controllers/trainingrecord.php create mode 100644 src/components/com_tjcertificate/site/events/record.php create mode 100644 src/components/com_tjcertificate/site/models/fields/createdby.php create mode 100644 src/components/com_tjcertificate/site/models/fields/users.php create mode 100644 src/components/com_tjcertificate/site/models/forms/trainingrecord.xml create mode 100644 src/components/com_tjcertificate/site/models/trainingrecord.php create mode 100644 src/components/com_tjcertificate/site/views/trainingrecord/index.html create mode 100644 src/components/com_tjcertificate/site/views/trainingrecord/tmpl/default.php create mode 100644 src/components/com_tjcertificate/site/views/trainingrecord/tmpl/edit.php create mode 100644 src/components/com_tjcertificate/site/views/trainingrecord/tmpl/edit.xml create mode 100644 src/components/com_tjcertificate/site/views/trainingrecord/view.html.php diff --git a/src/components/com_tjcertificate/administrator/access.xml b/src/components/com_tjcertificate/administrator/access.xml index 944f9715..d6adfd02 100644 --- a/src/components/com_tjcertificate/administrator/access.xml +++ b/src/components/com_tjcertificate/administrator/access.xml @@ -12,5 +12,8 @@ + + + diff --git a/src/components/com_tjcertificate/administrator/config.xml b/src/components/com_tjcertificate/administrator/config.xml index fafde553..028b934d 100644 --- a/src/components/com_tjcertificate/administrator/config.xml +++ b/src/components/com_tjcertificate/administrator/config.xml @@ -37,7 +37,19 @@ - + + + + + + + + + + + +
@@ -50,7 +62,15 @@ - + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + +
+
diff --git a/src/components/com_tjcertificate/administrator/controllers/certificate.php b/src/components/com_tjcertificate/administrator/controllers/certificate.php index 337faeb7..8c2414ba 100644 --- a/src/components/com_tjcertificate/administrator/controllers/certificate.php +++ b/src/components/com_tjcertificate/administrator/controllers/certificate.php @@ -24,6 +24,48 @@ */ class TjCertificateControllerCertificate extends FormController { + /** + * The client for which the templates are being created. + * + * @var string + * @since __DEPLOY_VERSION__ + */ + protected $client; + + /** + * The extension for which the templates are being created. + * + * @var string + * @since __DEPLOY_VERSION__ + */ + protected $extension; + + /** + * Constructor. + * + * @param array $config An optional associative array of configuration settings. + * + * @since __DEPLOY_VERSION__ + * @see JControllerLegacy + */ + public function __construct($config = array()) + { + parent::__construct($config); + + $app = Factory::getApplication(); + $jinput = $app->input; + + if (empty($this->extension)) + { + $this->extension = $jinput->get('extension', ''); + } + + if (empty($this->client)) + { + $this->client = $jinput->get('client', ''); + } + } + /** * Method to download issued certificate. * @@ -48,15 +90,22 @@ public function download() $certificate = TJCERT::Certificate(); + $certificateObj = $certificate::validateCertificate($uniqueCertificateId); + + // If $uniqueCertificateId is not valid then object is empty so need to handle error (CALL TO A MEMBER FUNCTION CANDOWNLOAD() ON BOOLEAN) + if (!$certificateObj->id) + { + $app->enqueueMessage(Text::_('JERROR_AN_ERROR_HAS_OCCURRED'), 'error'); + $app->redirect('index.php'); + } + // Check user having permission to download - if (!$certificate::canDownload($uniqueCertificateId)) + if (!$certificateObj->canDownload()) { $app->enqueueMessage(Text::_('JERROR_ALERTNOAUTHOR')); $app->redirect('index.php'); } - $certificateObj = $certificate::validateCertificate($uniqueCertificateId); - if (!$certificateObj->id) { $app->enqueueMessage(Text::_('COM_TJCERTIFICATE_ERROR_CERTIFICATE_EXPIRED'), 'error'); @@ -101,4 +150,53 @@ public function uploadCertificate() jexit(); } + + /** + * Gets the URL arguments to append to an item redirect. + * + * @param integer $recordId The primary key id for the item. + * @param string $urlVar The name of the URL variable for the id. + * + * @return string The arguments to append to the redirect URL. + * + * @since __DEPLOY_VERSION__ + */ + protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') + { + $append = parent::getRedirectToItemAppend($recordId); + + if (!empty ($this->extension)) + { + $append .= '&extension=' . $this->extension; + } + elseif (!empty ($this->client)) + { + $append .= '&client=' . $this->client; + } + + return $append; + } + + /** + * Gets the URL arguments to append to a list redirect. + * + * @return string The arguments to append to the redirect URL. + * + * @since __DEPLOY_VERSION__ + */ + protected function getRedirectToListAppend() + { + $append = parent::getRedirectToListAppend(); + + if (!empty ($this->extension)) + { + $append .= '&extension=' . $this->extension; + } + elseif (!empty ($this->client)) + { + $append .= '&client=' . $this->client; + } + + return $append; + } } diff --git a/src/components/com_tjcertificate/administrator/controllers/certificates.php b/src/components/com_tjcertificate/administrator/controllers/certificates.php index 6fc7e820..72fea524 100644 --- a/src/components/com_tjcertificate/administrator/controllers/certificates.php +++ b/src/components/com_tjcertificate/administrator/controllers/certificates.php @@ -12,6 +12,9 @@ defined('_JEXEC') or die('Restricted access'); use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\Utilities\ArrayHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Router\Route; /** * Certificate list controller class. @@ -34,4 +37,46 @@ public function getModel($name = 'Certificate', $prefix = 'TjCertificateModel') { return parent::getModel($name, $prefix, array('ignore_request' => true)); } + + /** + * Method to remove a record. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function delete() + { + $this->checkToken(); + + // Get items to remove from the request. + $cid = $this->input->get('cid', array(), 'array'); + + $extension = $this->input->getCmd('extension', null); + + if (!is_array($cid) || count($cid) < 1) + { + JError::raiseWarning(500, Text::_($this->text_prefix . '_NO_ITEM_SELECTED')); + } + else + { + // Get the model. + $model = $this->getModel(); + + // Make sure the item ids are integers + $cid = ArrayHelper::toInteger($cid); + + // Remove the items. + if ($model->delete($cid)) + { + $this->setMessage(Text::plural($this->text_prefix . '_N_ITEMS_DELETED', count($cid))); + } + else + { + $this->setMessage($model->getError()); + } + } + + $this->setRedirect(Route::_('index.php?option=com_tjcertificate&view=certificates', false)); + } } diff --git a/src/components/com_tjcertificate/administrator/controllers/template.json.php b/src/components/com_tjcertificate/administrator/controllers/template.json.php index 35ba7285..ff9c66d5 100644 --- a/src/components/com_tjcertificate/administrator/controllers/template.json.php +++ b/src/components/com_tjcertificate/administrator/controllers/template.json.php @@ -14,6 +14,8 @@ use Joomla\CMS\MVC\Controller\FormController; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; +use Joomla\CMS\Response\JsonResponse; +use Joomla\CMS\Session\Session; jimport('joomla.filesystem.folder'); @@ -84,4 +86,34 @@ public function loadDefaultTemplate() echo new JResponseJson($templateData); } } + + /** + * Function to load custom template + * + * @return object|void object + */ + public function loadCustomTemplate() + { + if (!Session::checkToken('get')) + { + echo new JsonResponse(null, Text::_('JINVALID_TOKEN'), true); + } + else + { + $app = Factory::getApplication(); + $input = $app->input; + $templateId = $input->get('templateId'); + + if (empty($templateId)) + { + echo new JsonResponse(null, Text::_('COM_TJCERTIFICATE_ERROR_SOMETHING_WENT_WRONG'), true); + + return; + } + + $tjCertificateTemplate = TJCERT::Template($templateId); + + echo new JsonResponse($tjCertificateTemplate->body); + } + } } diff --git a/src/components/com_tjcertificate/administrator/controllers/trainingrecord.json.php b/src/components/com_tjcertificate/administrator/controllers/trainingrecord.json.php new file mode 100644 index 00000000..23d4353c --- /dev/null +++ b/src/components/com_tjcertificate/administrator/controllers/trainingrecord.json.php @@ -0,0 +1,154 @@ + + * @copyright Copyright (C) 2009 - 2020 Techjoomla. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Registry\Registry; +use Joomla\Utilities\ArrayHelper; +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\CMS\Filesystem\Folder; +use Joomla\CMS\Filesystem\File; +use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Response\JsonResponse; +use Joomla\CMS\Table\Table; + +JLoader::import("/techjoomla/media/storage/local", JPATH_LIBRARIES); + +/** + * The Tj Certificate Training Record controller + * + * @since __DEPLOY_VERSION__ + */ +class TjCertificateControllerTrainingRecord extends FormController +{ + /** + * Function to delete the record attachment + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function deleteAttachment() + { + $app = Factory::getApplication(); + + if (!Session::checkToken()) + { + $app->enqueueMessage(Text::_('JINVALID_TOKEN'), 'error'); + echo new JsonResponse(null, null, true); + $app->close(); + } + + // Get the current user id + $user = Factory::getuser(); + + if (!$user->id) + { + return false; + } + + $clientId = $app->input->get('certificateId', 0, 'INT'); + $mediaId = $app->input->get('mediaId', 0, 'INT'); + + if (!$mediaId && !$clientId) + { + echo new JsonResponse(null, Text::_("JERROR_ALERTNOAUTHOR"), true); + $app->close(); + } + + $model = $this->getModel(); + $mediaPath = TJCERT::getMediaPath(); + $client = TJCERT::getClient(); + $result = $model->deleteMedia($mediaId, $mediaPath, $client, $clientId); + + if ($result) + { + echo new JResponseJson($result, Text::_('COM_TJCERTIFICATE_ATTACHMENT_DELETED_SUCCESSFULLY'), false); + $app->close(); + } + else + { + echo new JResponseJson(null, Text::_('COM_TJCERTIFICATE_ATTACHMENT_DELETED_FAILED'), true); + $app->close(); + } + } + + /** + * Method to delete the record from frontend. + * + * @return void|boolean + * + * @since __DEPLOY_VERSION__ + */ + public function delete() + { + $app = Factory::getApplication(); + + if (!Session::checkToken()) + { + $app->enqueueMessage(Text::_('JINVALID_TOKEN'), 'error'); + echo new JsonResponse(null, null, true); + $app->close(); + } + + $user = Factory::getUser(); + $mediaPath = TJCERT::getMediaPath(); + $client = TJCERT::getClient(); + $certificateId = $app->input->getInt('certificateId'); + $manageOwn = $user->authorise('certificate.external.manageown', $client); + $manage = $user->authorise('certificate.external.manage', $client); + + // If manageOwn permission then check record owner can only deleting own record + if ($manageOwn && !$manage) + { + $table = TJCERT::table("certificates"); + $table->load(array('id' => (int) $certificateId, 'user_id' => $user->id)); + + if (!$table->id) + { + echo new JsonResponse(null, Text::_('COM_TJCERTIFICATE_ERROR_SOMETHING_WENT_WRONG'), true); + $app->close(); + } + } + + $model = TJCERT::model('Certificate', array('ignore_request' => true)); + + if ($manageOwn || $manage) + { + // Remove the item + if ($model->delete($certificateId)) + { + // Delete media + $model = $this->getModel(); + JLoader::import("/techjoomla/media/tables/xref", JPATH_LIBRARIES); + $tableXref = Table::getInstance('Xref', 'TJMediaTable'); + $tableXref->load(array('client_id' => $certificateId)); + + if ($tableXref->media_id) + { + $model->deleteMedia($tableXref->media_id, $mediaPath, $client, $certificateId); + } + + echo new JResponseJson($result, Text::_('COM_TJCERTIFICATE_CERTIFICATE_DELETED_SUCCESSFULLY'), false); + $app->close(); + } + else + { + echo new JResponseJson(null, Text::_('COM_TJCERTIFICATE_CERTIFICATE_DELETED_FAILED'), true); + $app->close(); + } + } + } +} diff --git a/src/components/com_tjcertificate/administrator/controllers/trainingrecord.php b/src/components/com_tjcertificate/administrator/controllers/trainingrecord.php new file mode 100644 index 00000000..00a6e35b --- /dev/null +++ b/src/components/com_tjcertificate/administrator/controllers/trainingrecord.php @@ -0,0 +1,242 @@ + + * @copyright Copyright (C) 2009 - 2020 Techjoomla. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Registry\Registry; +use Joomla\Utilities\ArrayHelper; +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\CMS\Filesystem\Folder; +use Joomla\CMS\Filesystem\File; +use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Router\Route; + +JLoader::import("/techjoomla/media/storage/local", JPATH_LIBRARIES); + +/** + * The Tj Certificate Training Record controller + * + * @since __DEPLOY_VERSION__ + */ +class TjCertificateControllerTrainingRecord extends FormController +{ + /** + * Method to save a training record data. + * + * @param string $key The name of the primary key of the URL variable. + * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). + * + * @return boolean|void Incase of error boolean and in case of success void + * + * @since __DEPLOY_VERSION__ + */ + public function save($key = null, $urlVar = null) + { + // Check for request forgeries. + $this->checkToken(); + $app = Factory::getApplication(); + $user = Factory::getUser(); + $recordId = $app->input->getInt('id'); + $params = ComponentHelper::getParams('com_tjcertificate'); + + if (!$user->id) + { + throw new Exception(Text::_('JERROR_ALERTNOAUTHOR'), 403); + } + + $data = $app->input->get('jform', array(), 'array'); + + $model = $this->getModel(); + + // Validate the posted data. + $form = $model->getForm($data, false); + + if (!$form) + { + throw new \Exception($model->getError(), 500); + } + + $validData = $model->validate($form, $data); + + // Check for validation errors. + if ($validData === false) + { + // Get the validation messages. + $errors = $model->getErrors(); + + if (!empty($errors)) + { + // Push up to three validation messages out to the user. + for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++) + { + if ($errors[$i] instanceof Exception) + { + $app->enqueueMessage($errors[$i]->getMessage(), 'warning'); + } + else + { + $app->enqueueMessage($errors[$i], 'warning'); + } + } + } + + // Save the data in the session. + $app->setUserState('com_tjcertificate.edit.trainingrecord.data', $data); + + // Redirect back to the edit screen. + $this->setRedirect(Route::_('index.php?option=com_tjcertificate&view=trainingrecord&layout=edit&id=' . $recordId, false)); + + return false; + } + + if ($validData['assigned_user_id']) + { + $validData['user_id'] = $validData['assigned_user_id']; + } + else + { + $validData['user_id'] = $user->id; + } + + $validData['client'] = "external"; + $validData['state'] = $validData['state'] ? $validData['state'] : "-1"; + $validData['is_external'] = 1; + + $file = $app->input->files->get('jform', array(), 'array'); + + if (!empty($file['cert_file'])) + { + $validData['old_media_ids'] = $app->input->get('oldFiles', 0, 'INT'); + $uploadData = $model->uploadMedia($file, $validData); + $validData['cert_file'] = $uploadData['source']; + } + + $certificateModel = TJCERT::model('Certificate', array('ignore_request' => true)); + + $certificateModel->save($validData); + + $modelMediaXref = TJMediaXref::getInstance(); + + if ($uploadData['id']) + { + $mediaData['id'] = ''; + $mediaData['client_id'] = $certificateModel->getState('certificate.id'); + $mediaData['media_id'] = $uploadData['id']; + $mediaData['client'] = TJCERT::getClient(); + $modelMediaXref->bind($mediaData); + $modelMediaXref->save(); + } + + $this->setMessage(Text::_('COM_TJCERTIFICATE_TRAINING_RECORD_SAVE_SUCCESSFULLY')); + + if ($task === "apply") + { + // Redirect back to the edit screen. + $this->setRedirect( + Route::_('index.php?option=com_tjcertificate&view=trainingrecord&layout=edit&id=' . $certificateModel->getState('certificate.id'), false) + ); + } + + // Save task using to "Save & Close" action which is used only in backend + if ($task === "save") + { + // Redirect to the list screen. + $this->setRedirect( + Route::_('index.php?option=com_tjcertificate&view=certificates', false) + ); + } + + // Flush the data from the session. + $app->setUserState('com_tjcertificate.edit.trainingrecord.data', null); + } + + /** + * Cancel operation + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function cancel() + { + // Check for request forgeries. + $this->checkToken('request'); + + // Clear data from session. + \JFactory::getApplication()->setUserState('com_tjcertificate.edit.trainingrecord.data', null); + + $this->setRedirect(Route::_('index.php?option=com_tjcertificate&view=certificates&layout=my', false)); + } + + /** + * Downloads the file requested by user + * + * @return boolean|void + * + * @since __DEPLOY_VERSION__ + */ + public function downloadAttachment() + { + $app = Factory::getApplication(); + $user = Factory::getUser(); + + if (!$user->id) + { + throw new Exception(Text::_('JERROR_ALERTNOAUTHOR'), 403); + + return false; + } + + $clientId = $app->input->get('recordId', '', 'INT'); + $mediaId = $app->input->get('id', '', 'INT'); + + $manageOwn = $user->authorise('certificate.external.manageown', 'com_tjcertificate'); + $manage = $user->authorise('certificate.external.manage', 'com_tjcertificate'); + + // If manageOwn permission then check record owner can only download own record + if ($manageOwn && !$manage) + { + $table = TJCERT::table("certificates"); + $table->load(array('id' => (int) $clientId, 'user_id' => $user->id)); + + if (!$table->id) + { + throw new Exception(Text::_('JERROR_ALERTNOAUTHOR'), 403); + } + } + + $params = ComponentHelper::getParams('com_tjcertificate'); + + if (!$mediaId && !$clientId) + { + return false; + } + + $config = array(); + $config['mediaId'] = $mediaId; + + // Assign client id as Record Id + $config['client_id'] = $clientId; + $config['client'] = TJCERT::getClient(); + $mediaPath = TJCERT::getMediaPath(); + $mediaAttachmentData = TJMediaXref::getInstance($config); + $folderName = explode('.', $mediaAttachmentData->media->type); + + $downloadPath = JPATH_SITE . '/' . $mediaPath; + $downloadPath = $downloadPath . '/' . $folderName[0] . '/' . $mediaAttachmentData->media->source; + + $media = TJMediaStorageLocal::getInstance(); + $media->downloadMedia($downloadPath); + } +} diff --git a/src/components/com_tjcertificate/administrator/includes/tjcertificate.php b/src/components/com_tjcertificate/administrator/includes/tjcertificate.php index 70404a7d..3fc1b78c 100644 --- a/src/components/com_tjcertificate/administrator/includes/tjcertificate.php +++ b/src/components/com_tjcertificate/administrator/includes/tjcertificate.php @@ -15,6 +15,7 @@ use Joomla\CMS\Table\Table; use Joomla\String\StringHelper; use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\HTML\HTMLHelper; $language = JFactory::getLanguage(); $language->load('com_tjcertificate'); @@ -44,6 +45,10 @@ class TJCERT */ private static $config = null; + public static $client = "com_tjcertificate"; + + public static $mediaPath = "media/com_tjcertificate/external"; + /** * Retrieves a table from the table folder * @@ -130,4 +135,47 @@ public static function loadClass($className) return self::$loadedClass[$className]; } + + /** + * Initializes js lang constant dependencies + * + * @param string $location The location where the assets needs to load + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public static function init($location = 'site') + { + self::Language()->JsLanguageConstant(); + + if ($location == 'site') + { + HTMLHelper::stylesheet('media/com_tjcertificate/vendors/font-awesome-4.1.0/css/font-awesome.min.css'); + } + } + + /** + * Method to get client + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function getClient() + { + return self::$client; + } + + /** + * Method to get external media path + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function getMediaPath() + { + return self::$mediaPath; + } } diff --git a/src/components/com_tjcertificate/administrator/languages/en-GB/en-GB.com_tjcertificate.ini b/src/components/com_tjcertificate/administrator/languages/en-GB/en-GB.com_tjcertificate.ini index b52714cd..dd31d865 100644 --- a/src/components/com_tjcertificate/administrator/languages/en-GB/en-GB.com_tjcertificate.ini +++ b/src/components/com_tjcertificate/administrator/languages/en-GB/en-GB.com_tjcertificate.ini @@ -9,7 +9,7 @@ COM_TJCERTIFICATE_CONFIGURATION="TJ Certificate Configuration" COM_TJCERTIFICATE_GENEARAL_SETINGS="General" COM_TJCERTIFICATE_CERTIFICATE_PREFIX_LABEL="Certificate Prefix" COM_TJCERTIFICATE_CERTIFICATE_PREFIX_DESC="Unique Certificate Id Prefix" -COM_TJCERTIFICATE_CERTIFICATE_RANDOM_STRING_LENTGH_LABEL="Certificate random string length" +COM_TJCERTIFICATE_CERTIFICATE_RANDOM_STRING_LENTGH_LABEL="Certificate Random String Length" COM_TJCERTIFICATE_CERTIFICATE_RANDOM_STRING_LENTGH_DESC="Each generated certificate as a code. The format of the code is [Certificate Prefix]-[Random number]. This option sets the length of the random number. " @@ -233,7 +233,7 @@ COM_TJCERTIFICATE_BLOCKED_USER="Unknown / Deleted User" ; Certificate access COM_TJCERTIFICATE_SHOW_PUBLIC_PRIVATE="Set Certificate Privacy" COM_TJCERTIFICATE_SHOW_PUBLIC_PRIVATE_DESC="If 'Private' option is selected then only the owner is authorised to view the certificate" -COM_TJCERTIFICATE_SHOW_SEARCH_BOX="Show search box on certificate view" +COM_TJCERTIFICATE_SHOW_SEARCH_BOX="Show Search Box On Certificate View" COM_TJCERTIFICATE_SHOW_SEARCH_BOX_DESC="Select option 'Yes' to show search box" COM_TJCERTIFICATE_OPTION_PUBLIC="Public" COM_TJCERTIFICATE_OPTION_PRIVATE="Private" @@ -246,6 +246,14 @@ COM_TJCERTIFICATE_SOCIAL_SHARING_FACEBOOK="Facebook share" COM_TJCERTIFICATE_SOCIAL_SHARING_LINKEDIN="LinkedIn share" COM_TJCERTIFICATE_SOCIAL_SHARING_TWITTER="Twitter share" COM_TJCERTIFICATE_SOCIAL_SHARING_OPTIONS="Select social sharing options" +COM_TJCERTIFICATE_LINKEDIN_PROFILE_BTN="Enable Linkedin 'Add to profile' button" +COM_TJCERTIFICATE_LINKEDIN_PROFILE_BTN_DESC="Enable 'Add to profile' button to add the certificate in profile" +COM_TJCERTIFICATE_LINKEDIN_ORGANIZATION_INFO="Select Organisation name or Organisation Id to pass in Url" +COM_TJCERTIFICATE_LINKEDIN_ORGANIZATION_INFO_DESC="on option selection, link parameter will change accordingly" +COM_TJCERTIFICATE_LINKEDIN_ORGANIZATION_NAME="Organization Name" +COM_TJCERTIFICATE_LINKEDIN_ORGANIZATION_ID="Organization Id" +COM_TJCERTIFICATE_LINKEDIN_ORGANIZATION_ID_NAME="Add linkedIn organization name or Id" +COM_TJCERTIFICATE_LINKEDIN_ORGANIZATION_ID_NAME_DESC="Your organization ID (if your organization has an existing page on LinkedIn) or Your organization name (if your organization doesn’t have an existing page on LinkedIn)" ;Certificate Menu option COM_TJCERTIFICATE_CERTIFICATE_TEXT_FILTER="Show Free Text Filter" @@ -268,3 +276,87 @@ COM_TJCERTIFICATE_CERTIFICATE_FILTER_CERTIFICATE_TYPE_SELECT="- Select Type -" COM_TJCERTIFICATE_CLIENT_COM_TJLMS_COURSE="Course" COM_TJCERTIFICATE_CLIENT_COM_JTICKETING_EVENT="Event" COM_TJCERTIFICATE_CERTIFICATE_LIST_VIEW_TYPE="Type" + +;__DEPLOY_VERSION__ +;Global config - Certificate Image Generation +COM_TJCERTIFICATE_IMAGE_GENERATION="Certificate Image" +COM_TJCERTIFICATE_IMAGE_GENERATION_SETTINGS="Certificate Image Generation Settings" +COM_TJCERTIFICATE_IMAGE_GENERATION_TYPE="Image Generation Type" +COM_TJCERTIFICATE_IMAGE_GENERATION_TYPE_DESC="There are two ways certificate image can be generated.
1) By using JavaScript Canvas librabry
2) Using PHP ImageMagick library" +COM_TJCERTIFICATE_IMAGE_GENERATION_TYPE_CANVAS="HTML2Canvas Library" +COM_TJCERTIFICATE_IMAGE_GENERATION_TYPE_IMAGICK="PHP ImageMagick Library" +COM_TJCERTIFICATE_IMAGE_GENERATION_SETTINGS_NOTE="Please note:

" + +;__DEPLOY_VERSION__ +;Permission for external record +COM_TJCERTIFICATE_CLIENT_EXTERNAL="External" +COM_TJCERTIFICATE_PERMISSION_CREATE_EXTERNAL_CERTIFICATE="Create training records" +COM_TJCERTIFICATE_PERMISSION_CREATE_EXTERNAL_CERTIFICATE_DESC="Allow users to create training records" +COM_TJCERTIFICATE_PERMISSION_MANAGE_EXTERNAL_CERTIFICATE="Manage training records" +COM_TJCERTIFICATE_PERMISSION_MANAGE_EXTERNAL_CERTIFICATE_DESC="Allow users to manage training records" +COM_TJCERTIFICATE_PERMISSION_MANAGEOWN_EXTERNAL_CERTIFICATE="Manage own training records" +COM_TJCERTIFICATE_PERMISSION_MANAGEOWN_EXTERNAL_CERTIFICATE_DESC="Allow users to manage own training records" + +;__DEPLOY_VERSION__ +;params email, media +COM_TJCERTIFICATE_EMAIL_SETTINGS="Email Notifications" +COM_TJCERTIFICATE_ADMIN_EMAIL="Admin email address" +COM_TJCERTIFICATE_ADMIN_EMAIL_DESC="Add comma separated admin email address" +COM_TJCERTIFICATE_MEDIA_SETS="Media" +COM_TJCERTIFICATE_UPLOAD_MAX_SIZE="Maximum Size (in MB)" +COM_TJCERTIFICATE_UPLOAD_MAX_SIZE_DESC="The maximum size for an upload (in megabytes). Use zero for no limit. Note: your server has a maximum limit." +COM_TJCERTIFICATE_LEGAL_EXTENSION="Allowed Extensions (File Types)" +COM_TJCERTIFICATE_LEGAL_EXTENSION_DESC="Extensions (file types) you are allowed to upload (comma separated)." +COM_TJCERTIFICATE_N_RECORD_PUBLISHED="%d record(s) successfully published" +COM_TJCERTIFICATE_N_RECORD_UNPUBLISHED="%d record(s) successfully unpublished" + +;Date format +COM_TJCERTIFICATE_DATE_FORMAT_TO_SHOW="Date format" +COM_TJCERTIFICATE_DATE_FORMAT_TO_SHOW_DESC="Date format to show on site except filters having date format" +COM_TJCERTIFICATE_DATE_FORMAT_1="2012-01-02 01:30:00" +COM_TJCERTIFICATE_DATE_FORMAT_2="Mon, Jan 02 01:30 AM" +COM_TJCERTIFICATE_DATE_FORMAT_3="January 2, 2012, 1:30 am" +COM_TJCERTIFICATE_DATE_FORMAT_4="01.02.12" +COM_TJCERTIFICATE_DATE_FORMAT_5="2, 1, 2012" +COM_TJCERTIFICATE_DATE_FORMAT_6="01-30-00, 2-01-12" +COM_TJCERTIFICATE_DATE_FORMAT_7="01:30:00" +COM_TJCERTIFICATE_DATE_FORMAT_CUSTOM="Custom" +COM_TJCERTIFICATE_CUSTOM_DATE_FORMAT="Custom Date Format" +COM_TJCERTIFICATE_CUSTOM_DATE_FORMAT_DESC="This option will used to set custom php date format" + +;Training record form +COM_TJCERTIFICATE_FORM_LBL_CERTIFICATE_NAME="Name" +COM_TJCERTIFICATE_FORM_LBL_CERTIFICATE_NAME_DESC="Training record name" +COM_TJCERTIFICATE_CERTIFICATE_FORM_LBL_CERTIFICATE_ID="Record Id" +COM_TJCERTIFICATE_CERTIFICATE_FORM_LBL_CERTIFICATE_ID_DESC="Record Id" +COM_TJCERTIFICATE_FORM_LBL_CERTIFICATE_URL="Url
(start url with http/https)" +COM_TJCERTIFICATE_FORM_LBL_CERTIFICATE_URL_DESC="Add training record certificate Url" +COM_TJCERTIFICATE_FORM_LBL_ISSUE_ORG="Issuing Organization" +COM_TJCERTIFICATE_FORM_LBL_ISSUE_ORG_DESC="Organization Name" +COM_TJCERTIFICATE_CERTIFICATE_FORM_LBL_CERTIFICATE_ISSUED_DATE="Issue Date" +COM_TJCERTIFICATE_CERTIFICATE_FORM_LBL_CERTIFICATE_ISSUED_DATE_DESC="Add training record completion or certificate issue date" +COM_TJCERTIFICATE_CERTIFICATE_FORM_LBL_CERTIFICATE_EXPIRY_DATE="Expiration Date" +COM_TJCERTIFICATE_CERTIFICATE_FORM_LBL_CERTIFICATE_EXPIRY_DATE_DESC="Certificate expiry date" +COM_TJCERTIFICATE_CERTIFICATE_FORM_LBL_CERTIFICATE_STATUS="Record Status" +COM_TJCERTIFICATE_CERTIFICATE_FORM_LBL_CERTIFICATE_STATUS_DESC="Select record status" +COM_TJCERTIFICATE_CERTIFICATE_FORM_STATUS_ATTENDED="Attended" +COM_TJCERTIFICATE_CERTIFICATE_FORM_STATUS_COMPLETED="Completed" +COM_TJCERTIFICATE_CERTIFICATE_FORM_STATUS_PASSED="Passed" +COM_TJCERTIFICATE_CERTIFICATE_FORM_STATUS_FAILED="Failed" +COM_TJCERTIFICATE_FORM_LBL_CERTIFICATE_FILE="Certificate File
pdf/image" +COM_TJCERTIFICATE_FORM_LBL_CERTIFICATE_FILE_DESC="Upload certificate pdf/image" +COM_TJCERTIFICATE_EXTERNAL_CERTIFICATE_DETAIL_VIEW_HEAD="Training Record" +COM_TJCERTIFICATE_MEDIA_UPLOAD_ERROR="Uploaded media exceeds the allowed file size or it is an unsupported file." +COM_TJCERTIFICATE_MEDIA_INVALID_FILE_TYPE="Invalid file type. Please enter the allowed file types." +COM_TJCERTIFICATE_CONFIRM_DELETE_ATTACHMENT="Are you sure you want to delete this attachement?" +COM_TJCERTIFICATE_ATTACHMENT_DELETE="Delete attachment" +COM_TJCERTIFICATE_ATTACHMENT_DELETED_SUCCESSFULLY="Attachment deleted successfully" +COM_TJCERTIFICATE_ATTACHMENT_DELETED_FAILED="Failed to delete attachment. Please try again after some time." +COM_TJCERTIFICATE_ADD_EXTERNAL_CERTIFICATE="New Training Record" +COM_TJCERTIFICATE_PAGE_ADD_TRAINING_RECORD="Training Record: New" +COM_TJCERTIFICATE_PAGE_EDIT_TRAINING_RECORD="Training Record: Edit" +COM_TJCERTIFICATE_CERTIFICATE_FRONTEND_PREVIEW="Frontend Preview" +COM_TJCERTIFICATE_TRAINING_RECORD_SAVE_SUCCESSFULLY="Training record saved successfully" +COM_TJCERTIFICATE_STATUS_PENDING="Pending" +COM_TJCERTIFICATE_EXPIRY_DATE_VALIDATION_MESSAGE="Expiration date should be greater than issue date" +COM_TJCERTIFICATE_LBL_CERTIFICATE_URL="Url" diff --git a/src/components/com_tjcertificate/administrator/languages/en-GB/en-GB.com_tjcertificate.sys.ini b/src/components/com_tjcertificate/administrator/languages/en-GB/en-GB.com_tjcertificate.sys.ini index b53b1258..061c19f1 100644 --- a/src/components/com_tjcertificate/administrator/languages/en-GB/en-GB.com_tjcertificate.sys.ini +++ b/src/components/com_tjcertificate/administrator/languages/en-GB/en-GB.com_tjcertificate.sys.ini @@ -29,3 +29,6 @@ COM_TJCERTIFICATE_CERTIFICATE_LIST_DEFAULT_TITLE="Certificates" COM_TJCERTIFICATE_CERTIFICATE_LIST_DEFAULT_DESC="List of certificates" COM_TJCERTIFICATE_CERTIFICATE_FIELDSET_CLIENT_SELECT_LABEL="Client" COM_TJCERTIFICATE_CERTIFICATE_FIELDSET_CLIENT_SELECT_DESC="Client" + +;Add record menu +COM_TJCERTIFICATE_ADD_EXTERNAL_CERTIFICATE="Add training record" diff --git a/src/components/com_tjcertificate/administrator/layouts/preview.php b/src/components/com_tjcertificate/administrator/layouts/preview.php new file mode 100644 index 00000000..b88239d0 --- /dev/null +++ b/src/components/com_tjcertificate/administrator/layouts/preview.php @@ -0,0 +1,39 @@ + + * @copyright Copyright (C) 2009 - 2020 Techjoomla. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Language\Text; + +?> + + + diff --git a/src/components/com_tjcertificate/administrator/libraries/certificate.php b/src/components/com_tjcertificate/administrator/libraries/certificate.php index 503b063a..910f8b9e 100644 --- a/src/components/com_tjcertificate/administrator/libraries/certificate.php +++ b/src/components/com_tjcertificate/administrator/libraries/certificate.php @@ -21,6 +21,9 @@ use Joomla\Registry\Registry; use Dompdf\Dompdf; use Dompdf\Options; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Uri\Uri; /** * Certificate class. Handles all application interaction with a Certificate @@ -57,8 +60,26 @@ class TjCertificateCertificate extends CMSObject public $defaultCertPrefix = "CERT"; + public $certImageDir = JPATH_SITE . '/media/com_tjcertificate/certificates/'; + + public $certTmpDir = JPATH_SITE . '/media/com_tjcertificate/tmp/'; + protected static $certificateObj = array(); + public $is_external = 0; + + public $name = null; + + public $cert_url = ""; + + public $cert_file = ""; + + public $issuing_org = ""; + + public $status = ""; + + public $created_by = ""; + /** * Constructor activating the default information of the Certificate * @@ -282,6 +303,32 @@ public function getComment() return $this->comment; } + /** + * Set certificate issue date + * + * @param string $value comment. + * + * @return void. + * + * @since __DEPLOY_VERSION__ + */ + public function setIssuedDate($value = null) + { + $this->issued_on = $value; + } + + /** + * Get certificate issue date + * + * @return string comment + * + * @since __DEPLOY_VERSION__ + */ + public function getIssuedDate() + { + return $this->issued_on; + } + /** * Returns the global Certificate object * @@ -428,6 +475,13 @@ public function save() $table->issued_on = Factory::getDate()->toSql(); } + // If certificate id is not added from the form then add + if (empty($this->unique_certificate_id)) + { + $options = new Registry; + $table->unique_certificate_id = $this->generateUniqueCertId($options); + } + // Store the user data in the database if (!($table->store())) { @@ -438,9 +492,19 @@ public function save() $this->id = $table->id; - // Fire the onTjCertificateAfterSave event. $dispatcher = \JEventDispatcher::getInstance(); + if ($table->is_external && $isNew) + { + /* Send mail on record creation */ + JLoader::import('components.com_tjcertificate.events.record', JPATH_SITE); + $tjCertificateTriggerRecord = new TjCertificateTriggerRecord; + $tjCertificateTriggerRecord->onAfterRecordSave($this, true); + $dispatcher->trigger('onTrainingRecordAfterAdded', array($isNew, $this)); + } + + // Fire the onTjCertificateAfterSave event. + $dispatcher->trigger('onTjCertificateAfterSave', array($isNew, $this)); } catch (\Exception $e) @@ -471,14 +535,33 @@ public function bind(&$array) return false; } - // Bind the array - if (!$this->setProperties($array)) + $getPrivateProperties = $this->_getPrivateProperties(); + + $getPublicProperties = $this->_getPublicProperties(); + + $publicProperties = array(); + + foreach ($getPublicProperties as $key => $value) + { + $publicProperties[$value->name] = ''; + } + + $setPublicProperties = array_intersect_key($array, $publicProperties); + + // Set public properties + if (!$this->setProperties($setPublicProperties)) { $this->setError(Text::_('COM_TJCERTIFICATE_BINDING_ERROR')); return false; } + // Set private properties + foreach ($getPrivateProperties as $key => $value) + { + $this->{$value->name} = $array[$value->name]; + } + // Make sure its an integer $this->id = (int) $this->id; @@ -544,13 +627,22 @@ public static function getIssued($client, $clientId, $userId = 0, $expired = fal * * @param boolean $showSearchBox Show search box * + * @param boolean $isExternal Check record is external + * * @return string Certificate url. * * @since 1.0 */ - public function getUrl($options, $showSearchBox = true) + public function getUrl($options, $showSearchBox = true, $isExternal = false) { - $url = 'index.php?option=com_tjcertificate&view=certificate&certificate=' . $this->unique_certificate_id; + if ($isExternal) + { + $url = 'index.php?option=com_tjcertificate&view=trainingrecord&id=' . $this->id; + } + else + { + $url = 'index.php?option=com_tjcertificate&view=certificate&certificate=' . $this->unique_certificate_id; + } // If search box is true then only show search box param in URL if ($showSearchBox) @@ -565,7 +657,7 @@ public function getUrl($options, $showSearchBox = true) if (isset($options['absolute'])) { - return JUri::root() . substr(Route::_($url), strlen(JUri::base(true)) + 1); + return Route::link('site', $url, false, 0, true); } return Route::_($url); @@ -605,7 +697,7 @@ public function getDownloadUrl($options = array()) /** * Method to get certificate download url. * - * @param boolean $store Store as attachment for emails + * @param integer $store Store as attachment for emails * * @return boolean|string Certificate pdf url. * @@ -697,6 +789,10 @@ public function pdfDownload($store = 0) readfile($certificatePdfName); jexit(); } + elseif ($store == 2) + { + return $domPDF->output(); + } $domPDF->stream($certificatePdfName, array("Attachment" => 1)); @@ -769,8 +865,11 @@ public function issueCertificate($replacements, $options) throw new Exception(Text::_('COM_TJCERTIFICATE_TEMPLATE_INVALID')); } - // Generate unique certificate id - $this->unique_certificate_id = $this->generateUniqueCertId($options); + if (empty($this->unique_certificate_id)) + { + // Generate unique certificate id + $this->unique_certificate_id = $this->generateUniqueCertId($options); + } // Generate unique certificate id replacement $replacements->certificate->cert_id = $this->unique_certificate_id; @@ -808,9 +907,28 @@ public function issueCertificate($replacements, $options) } // Save certificate - $this->save(); + if ($this->save()) + { + // Remove old certificate image after re-generating the certificate + $path = JPATH_SITE . '/media/com_tjcertificate/certificates/'; + $fileName = $this->unique_certificate_id . '.png'; + + if (JFile::exists($path . $fileName)) + { + JFile::delete($path . $fileName); + } + + // Generate Certificate Image + $params = ComponentHelper::getParams('com_tjcertificate'); + + if ($params->get('cert_image_gen_type') == 'imagick') + { + // Generate image from PDF + $this->generateImageFromPDF($this->pdfDownload(2)); + } - return self::getInstance($this->id); + return self::getInstance($this->id); + } } catch (\Exception $e) { @@ -943,15 +1061,13 @@ protected function generateUniqueCertId($options) } /** - * This function checks the certificate download permission - * - * @param STRING $uniqueCertificateId certificate Id + * This function checks the certificate download permission * * @return boolean * * @since __DEPLOY_VERSION__ */ - public static function canDownload($uniqueCertificateId) + public function canDownload() { $user = Factory::getUser(); @@ -962,10 +1078,7 @@ public static function canDownload($uniqueCertificateId) if ($user->authorise('certificate.download.own', 'com_tjcertificate')) { - $table = TJCERT::table("certificates"); - $table->load(array('unique_certificate_id' => $uniqueCertificateId)); - - if ($user->get('id') == $table->user_id) + if ($user->get('id') == $this->user_id) { return true; } @@ -973,4 +1086,115 @@ public static function canDownload($uniqueCertificateId) return false; } + + /** + * Method to get linkedIn add to profile url. + * + * @return STRING + * + * @since __DEPLOY_VERSION__ + */ + public function getAddToLinkedInProfileUrl() + { + $params = ComponentHelper::getParams('com_tjcertificate'); + $config = Factory::getConfig(); + $siteName = $config->get('sitename'); + + $issuedMonth = HTMLHelper::_('date', $this->issued_on, 'm'); + $issuedYear = HTMLHelper::_('date', $this->issued_on, 'Y'); + + $expirationDetails = null; + + if ($this->expired_on != '0000-00-00 00:00:00') + { + $expirationMonth = HTMLHelper::_('date', $this->expired_on, 'm'); + $expirationYear = HTMLHelper::_('date', $this->expired_on, 'Y'); + $expirationDetails = '&expirationYear=' . $expirationYear . '&expirationMonth=' . $expirationMonth; + } + + $orgParam = '&' . $params->get('organization_info') . '=' . $params->get('organization_id_name'); + + // Get client data + $dispatcher = JDispatcher::getInstance(); + PluginHelper::importPlugin('content'); + $result = $dispatcher->trigger('getCertificateClientData', array($this->client_id, $this->client)); + $clientData = $result[0]; + + $urlOptions = array(); + $urlOptions['absolute'] = true; + $certificateUrl = $this->getURL($urlOptions, false); + + $certificateTitle = $clientData->title ? $clientData->title : $siteName . ' ' . Text::_('COM_TJCERTIFICATE_CERTIFICATE_DETAIL_VIEW_HEAD'); + $linkedInprofileUrl = 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&name=' . $certificateTitle . $orgParam + . '&issueYear=' . $issuedYear . '&issueMonth=' . $issuedMonth . $expirationDetails + . '&certUrl=' . urlencode($certificateUrl) . '&certId=' . $this->unique_certificate_id; + + return $linkedInprofileUrl; + } + + /** + * Method to generate certificate image from PDF. + * + * @param string $domPDFOutput DomPDF output. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function generateImageFromPDF($domPDFOutput) + { + if (extension_loaded('imagick')) + { + if (!JFolder::exists($this->certImageDir)) + { + JFolder::create($this->certImageDir); + } + + if (!JFolder::exists($this->certTmpDir)) + { + JFolder::create($this->certTmpDir); + } + + $tmpPDF = $this->certTmpDir . $this->unique_certificate_id . '.pdf'; + + file_put_contents($tmpPDF, $domPDFOutput); + + $im = new Imagick; + $im->setResolution(72, 72); + $im->readimage($tmpPDF); + $im->setImageBackgroundColor('white'); + $im->setImageAlphaChannel(imagick::ALPHACHANNEL_REMOVE); + $im->mergeImageLayers(imagick::LAYERMETHOD_FLATTEN); + $im->writeImage($this->certImageDir . $this->unique_certificate_id . '.png'); + $im->clear(); + $im->destroy(); + + if (JFile::exists($tmpPDF)) + { + JFile::delete($tmpPDF); + } + } + } + + /** + * Get formated date + * + * @param string $datetime The current offset + * + * @return string formatted datetime + * + * @since __DEPLOY_VERSION__ + **/ + public function getFormatedDate($datetime) + { + $params = ComponentHelper::getParams('com_tjcertificate'); + $dateFormat = $params->get('date_format_show'); + + if ($dateFormat == "custom") + { + $dateFormat = $params->get('custom_format'); + } + + return HTMLHelper::_('date', $datetime, $dateFormat, true); + } } diff --git a/src/components/com_tjcertificate/administrator/libraries/language.php b/src/components/com_tjcertificate/administrator/libraries/language.php new file mode 100644 index 00000000..02298065 --- /dev/null +++ b/src/components/com_tjcertificate/administrator/libraries/language.php @@ -0,0 +1,38 @@ + + * @copyright Copyright (C) 2009 - 2020 Techjoomla. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +defined('_JEXEC') or die(); + +use Joomla\CMS\Language\Text; + +/** + * TJCertificate language constant class for common methods + * + * @since _DEPLOY_VERSION_ + */ +class TJCertificateLanguage +{ + /** + * Language constants to be used in js + * + * @return void + * + * @since _DEPLOY_VERSION_ + */ + public function JsLanguageConstant() + { + // Tjmedia js + Text::script('COM_TJCERTIFICATE_CONFIRM_DELETE_ATTACHMENT'); + Text::script('COM_TJCERTIFICATE_MEDIA_INVALID_FILE_TYPE'); + Text::script('COM_TJCERTIFICATE_MEDIA_UPLOAD_ERROR'); + Text::script('COM_TJCERTIFICATE_DELETE_CERTIFICATE_MESSAGE'); + Text::script('COM_TJCERTIFICATE_EXPIRY_DATE_VALIDATION_MESSAGE'); + } +} diff --git a/src/components/com_tjcertificate/administrator/libraries/mails.php b/src/components/com_tjcertificate/administrator/libraries/mails.php new file mode 100644 index 00000000..e34435f3 --- /dev/null +++ b/src/components/com_tjcertificate/administrator/libraries/mails.php @@ -0,0 +1,175 @@ + + * @copyright Copyright (C) 2009 - 2020 Techjoomla. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +defined('_JEXEC') or die; +jimport('techjoomla.tjnotifications.tjnotifications'); + +use Joomla\CMS\Factory; +use Joomla\CMS\User\User; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Component\ComponentHelper; +use Joomla\Registry\Registry; + +/** + * Class TjCertificateMails + * + * @since __DEPLOY_VERSION__ + */ +class TjCertificateMails +{ + protected $params; + + protected $siteConfig; + + protected $sitename; + + protected $siteadminname; + + protected $user; + + protected $client; + + protected $tjnotifications; + + protected $siteinfo; + + /** + * Method acts as a consturctor + * + * @since __DEPLOY_VERSION__ + */ + public function __construct() + { + $this->params = ComponentHelper::getParams('com_tjcertificate'); + $this->siteConfig = Factory::getConfig(); + $this->sitename = $this->siteConfig->get('sitename'); + $this->siteadminname = $this->siteConfig->get('fromname'); + $this->user = Factory::getUser(); + $this->client = TJCERT::getClient(); + $this->tjnotifications = new Tjnotifications; + + $this->siteinfo = new stdClass; + $this->siteinfo->sitename = $this->sitename; + } + + /** + * Send mails when record is created + * + * @param OBJECT $recordDetails Record Detail + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function onAfterCreateRecord($recordDetails) + { + $adminRecipients = array(); + $db = Factory::getDBO(); + + // Get all admin users + $query = $db->getQuery(true); + + $query->select('id'); + $query->from($db->quoteName('#__users')); + $query->where($db->quoteName('sendEmail') . '= 1'); + $db->setQuery($query); + $adminUsers = $db->loadObjectList(); + + foreach ($adminUsers as $adminUser) + { + $adminRecipients[] = Factory::getUser($adminUser->id); + $adminRecipients['email']['to'][] = Factory::getUser($adminUser->id)->email; + } + + $userEmailArray = array(); + + if ($recordDetails->getUserId()) + { + $user = Factory::getUser($recordDetails->getUserId()); + $userEmailArray[] = $user->email; + } + + $recipients = array('email' => array('to' => $userEmailArray)); + + $adminkey = "createRecordMailToAdmin"; + $userkey = "createRecordMailToUser"; + + $siteInfo = new stdClass; + $siteInfo->sitename = $this->sitename; + + $replacements = new stdClass; + $replacements->info = $this->siteinfo; + $replacements->record = $recordDetails; + $replacements->user = $user; + + $options = new Registry; + $options->set('record', $recordDetails); + + // Mail to User after record added on behalf + + if ($recordDetails->getUserId() != $recordDetails->created_by) + { + $assignUserkey = "assignRecordMailToUser"; + $replacements->assigner = Factory::getUser($recordDetails->created_by); + + $this->tjnotifications->send($this->client, $assignUserkey, $recipients, $replacements, $options); + } + else + { + // Mail to site admin + $this->tjnotifications->send($this->client, $adminkey, $adminRecipients, $replacements, $options); + + // Mail to User + $this->tjnotifications->send($this->client, $userkey, $recipients, $replacements, $options); + } + + return; + } + + /** + * Send mail when record is edited + * + * @param OBJECT $recordDetails Record Detail + * + * @param int $isPublished Record state + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function onAfterRecordStateChange($recordDetails, $isPublished) + { + $userEmailArray = array(); + + if ($recordDetails->user_id) + { + $recordOwner = Factory::getUser($recordDetails->user_id); + $userEmailArray[] = $recordOwner->email; + } + + $recipients = array('email' => array('to' => $userEmailArray)); + + $replacements = new stdClass; + $replacements->info = $this->siteinfo; + $replacements->record = $recordDetails; + $replacements->user = $recordOwner; + + $siteinfo = new stdClass; + $siteinfo->sitename = $this->sitename; + + $options = new Registry; + $options->set('record', $recordDetails); + + // Mail to record owner + $this->tjnotifications->send($this->client, "recordApprovedMailToUser", $recipients, $replacements, $options); + + return; + } +} diff --git a/src/components/com_tjcertificate/administrator/models/certificate.php b/src/components/com_tjcertificate/administrator/models/certificate.php index 0b8e3bf3..8ef13993 100644 --- a/src/components/com_tjcertificate/administrator/models/certificate.php +++ b/src/components/com_tjcertificate/administrator/models/certificate.php @@ -15,6 +15,7 @@ use \Joomla\CMS\MVC\Model\AdminModel; use Joomla\CMS\Table\Table; use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Registry\Registry; /** * Item Model for an Certificate. @@ -23,6 +24,29 @@ */ class TjCertificateModelCertificate extends AdminModel { + /** + * Method to get a certificate. + * + * @param integer $pk An optional id of the object to get, otherwise the id from the model state is used. + * + * @return mixed certificate data object on success, false on failure. + * + * @since __DEPLOY_VERSION__ + */ + public function getItem($pk = null) + { + if ($result = parent::getItem($pk)) + { + // Prime required properties. + if (empty($result->id)) + { + $result->client = $this->getState('certificate.client'); + } + } + + return $result; + } + /** * Method to get the record form. * @@ -130,6 +154,18 @@ protected function populateState() $jinput = Factory::getApplication()->input; $id = ($jinput->get('id'))?$jinput->get('id'):$jinput->get('id'); $this->setState('certificate.id', $id); + + $client = $jinput->get('client', ''); + $extension = $jinput->get('extension', ''); + + if (!empty($extension)) + { + $this->setState('certificate.client', $extension); + } + else + { + $this->setState('certificate.client', $client); + } } /** @@ -152,4 +188,99 @@ public function getCertificateProviderInfo($contentId, $client) return trim(implode("\n", $html)); } + + /** + * Method to delete record + * + * @param array $certificateIds post data + * + * @return boolean True on success. + * + * @since __DEPLOY_VERSION__ + */ + public function delete($certificateIds) + { + $certificateIds = (array) $certificateIds; + $table = $this->getTable('Certificates'); + + foreach ($certificateIds as $certificateId) + { + $table->load(array('id' => (int) $certificateId)); + + if ($table->delete($table->id)) + { + if ($table->is_external) + { + $dispatcher = \JEventDispatcher::getInstance(); + $dispatcher->trigger('onTrainingRecordAfterDelete', array($table)); + } + + // Delete media + $model = TJCERT::model('TrainingRecord', array('ignore_request' => true)); + JLoader::import("/techjoomla/media/tables/xref", JPATH_LIBRARIES); + $tableXref = Table::getInstance('Xref', 'TJMediaTable'); + $tableXref->load(array('client_id' => $table->id)); + $mediaPath = TJCERT::getMediaPath(); + $client = TJCERT::getClient(); + + if ($tableXref->media_id) + { + $model->deleteMedia($tableXref->media_id, $mediaPath, $client, $table->id); + } + } + } + + return true; + } + + /** + * Publish the element + * + * @param array $ids Item id + * + * @param int $state Publish state + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + public function publish($ids, $state = 1) + { + $table = $this->getTable(); + + foreach ($ids as $id) + { + $table->load($id); + $oldState = $table->state; + $table->state = $state; + + if ($table->store()) + { + if ($table->is_external) + { + JLoader::import('components.com_tjcertificate.events.record', JPATH_SITE); + $tjCertificateTriggerRecord = new TjCertificateTriggerRecord; + + // If record state is pending then only send the approval email + if ($oldState == -1) + { + $tjCertificateTriggerRecord->onRecordStateChange($table, $table->state); + } + + $dispatcher = \JEventDispatcher::getInstance(); + + if ($table->state == 1) + { + $dispatcher->trigger('onTrainingRecordAfterPublished', array($table)); + } + elseif ($table->state == 0) + { + $dispatcher->trigger('onTrainingRecordAfterUnpublished', array($table)); + } + } + } + } + + return true; + } } diff --git a/src/components/com_tjcertificate/administrator/models/certificates.php b/src/components/com_tjcertificate/administrator/models/certificates.php index 78ce5435..a5074d46 100644 --- a/src/components/com_tjcertificate/administrator/models/certificates.php +++ b/src/components/com_tjcertificate/administrator/models/certificates.php @@ -81,6 +81,7 @@ protected function getListQuery() // Initialize variables. $db = $this->getDbo(); $query = $db->getQuery(true); + $app = Factory::getApplication(); $extension = Factory::getApplication()->input->get('extension', '', 'CMD'); @@ -172,7 +173,11 @@ protected function getListQuery() } elseif ($state === '') { - $query->where('(ci.state = 0 OR ci.state = 1)'); + // Publish, Unpublish and Pending records available in frontend + if ($app->isSite()) + { + $query->where('(ci.state IN (0,1,-1))'); + } } // Filter by Expired certificates diff --git a/src/components/com_tjcertificate/administrator/models/fields/certificatetemplates.php b/src/components/com_tjcertificate/administrator/models/fields/certificatetemplates.php index a738626f..8b03b2b7 100644 --- a/src/components/com_tjcertificate/administrator/models/fields/certificatetemplates.php +++ b/src/components/com_tjcertificate/administrator/models/fields/certificatetemplates.php @@ -10,6 +10,7 @@ JFormHelper::loadFieldClass('list'); use Joomla\CMS\Language\Text; +use Joomla\CMS\Factory; JLoader::import('components.com_tjcertificate.includes.tjcertificate', JPATH_ADMINISTRATOR); @@ -31,11 +32,17 @@ protected function getOptions() { $options = array(); - $user = JFactory::getUser(); - $db = JFactory::getDbo(); + $app = Factory::getApplication()->input; + $user = Factory::getUser(); + $db = Factory::getDbo(); $client = $this->getAttribute('client'); + if (empty($client)) + { + $client = $app->get('extension', ''); + } + $options[] = JHtml::_('select.option', '', Text::_('COM_TJCERTIFICATE_CERTIFICATE_TEMPLATE_FIELD_SELECT')); // Get Private/Created by logged-in user's templates diff --git a/src/components/com_tjcertificate/administrator/models/forms/certificate.xml b/src/components/com_tjcertificate/administrator/models/forms/certificate.xml index d1fafc51..6b4b23da 100644 --- a/src/components/com_tjcertificate/administrator/models/forms/certificate.xml +++ b/src/components/com_tjcertificate/administrator/models/forms/certificate.xml @@ -13,12 +13,7 @@ @@ -51,6 +46,7 @@ diff --git a/src/components/com_tjcertificate/administrator/models/forms/filter_certificates.xml b/src/components/com_tjcertificate/administrator/models/forms/filter_certificates.xml index f5b6dcbd..0017c2dc 100644 --- a/src/components/com_tjcertificate/administrator/models/forms/filter_certificates.xml +++ b/src/components/com_tjcertificate/administrator/models/forms/filter_certificates.xml @@ -41,14 +41,27 @@ --> + + + + + + + diff --git a/src/components/com_tjcertificate/administrator/models/forms/filter_templates.xml b/src/components/com_tjcertificate/administrator/models/forms/filter_templates.xml index 4a45dbc2..76d7b813 100644 --- a/src/components/com_tjcertificate/administrator/models/forms/filter_templates.xml +++ b/src/components/com_tjcertificate/administrator/models/forms/filter_templates.xml @@ -11,14 +11,13 @@ /> - - + addfieldpath="/components/com_tjcertificate/models/fields" + clientByUser="1" + onchange="this.form.submit();" /> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/src/components/com_tjcertificate/administrator/models/trainingrecord.php b/src/components/com_tjcertificate/administrator/models/trainingrecord.php new file mode 100644 index 00000000..9fa80adf --- /dev/null +++ b/src/components/com_tjcertificate/administrator/models/trainingrecord.php @@ -0,0 +1,290 @@ + + * @copyright Copyright (C) 2009 - 2019 Techjoomla. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\AdminModel; +use Joomla\CMS\Table\Table; +use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\MVC\Model\BaseDatabaseModel; +use Joomla\CMS\Language\Text; + +JLoader::import("/techjoomla/media/storage/local", JPATH_LIBRARIES); + +/** + * TjCertificate Training Record Model. + * + * @since __DEPLOY_VERSION__ + */ +class TjCertificateModelTrainingRecord extends AdminModel +{ + /** + * @var null Item data + * @since __DEPLOY_VERSION__ + */ + protected $item = null; + + /** + * Method to get a certificate. + * + * @param integer $pk An optional id of the object to get, otherwise the id from the model state is used. + * + * @return mixed certificate data object on success, false on failure. + * + * @since __DEPLOY_VERSION__ + */ + public function getItem($pk = null) + { + if ($item = parent::getItem($pk)) + { + if (!empty($item->id)) + { + // Do any procesing on fields here if needed + BaseDatabaseModel::addIncludePath(JPATH_SITE . '/libraries/techjoomla/media/models'); + + // Create TJMediaXref class object + $modelMediaXref = BaseDatabaseModel::getInstance('Xref', 'TJMediaModel', array('ignore_request' => true)); + $modelMediaXref->setState('filter.clientId', $item->id); + $modelMediaXref->setState('filter.client', 'com_tjcertificate'); + $mediaData = $modelMediaXref->getItems(); + + $item->mediaData = $mediaData; + } + } + + return $item; + } + + /** + * Method to get the record form. + * + * @param array $data Data for the form. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * + * @return JForm|boolean A JForm object on success, false on failure + * + * @since __DEPLOY_VERSION__ + */ + public function getForm($data = array(), $loadData = true) + { + // Get the form. + $form = $this->loadForm('com_tjcertificate.trainingrecord', 'trainingrecord', array('control' => 'jform', 'load_data' => $loadData)); + + return empty($form) ? false : $form; + } + + /** + * Returns a Table object, always creating it. + * + * @param string $type The table type to instantiate + * @param string $prefix A prefix for the table class name. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JTable A database object + */ + public function getTable($type = 'Certificates', $prefix = 'TjCertificateTable', $config = array()) + { + Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_tjcertificate/tables'); + + return Table::getInstance($type, $prefix, $config); + } + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since __DEPLOY_VERSION__ + */ + protected function loadFormData() + { + // Check the session for previously entered form data. + $data = Factory::getApplication()->getUserState('com_tjcertificate.edit.trainingrecord.data', array()); + + if (empty($data)) + { + $data = $this->getItem(); + $data->assigned_user_id = $data->user_id; + } + + return $data; + } + + /** + * Method to upload file for timelog activity + * + * @param Array $file File field array + * + * @param array $data The form data + * + * @return array + * + * @since __DEPLOY_VERSION__ + */ + public function uploadMedia($file, $data) + { + $user = Factory::getUser(); + + $params = ComponentHelper::getParams('com_tjcertificate'); + + if (!empty($file['cert_file'])) + { + $filePath = TJCERT::getMediaPath(); + $uploadedFileExtension = strtolower($params->get('upload_extensions', '', 'STRING')); + $fileExtensionType = explode(',', $uploadedFileExtension); + + $config = array(); + $config['type'] = $fileExtensionType; + $config['size'] = $params->get('upload_maxsize', '10'); + $config['auth'] = true; + + if (!empty($file['cert_file']['name'])) + { + $fileType = explode("/", $file['cert_file']['type']); + $config['title'] = $file['cert_file']['name']; + $config['uploadPath'] = JPATH_SITE . '/' . $filePath . '/' . strtolower($fileType[0]); + + $media = TJMediaStorageLocal::getInstance($config); + $mediaData = $media->upload(array($file['cert_file'])); + + if (!empty($media->getError())) + { + $errorFiles[] = $media->getError() . ' (' . $attachments['media_file']['name'] . ')'; + } + elseif ($mediaData[0]['id']) + { + $uploadedMediaId = $mediaData[0]; + + if (!empty($data['old_media_ids'])) + { + if ($data['old_media_ids'] != $mediaId) + { + $this->deleteMedia($data['old_media_ids'], $filePath, 'com_tjcertificate', $data['id']); + } + } + } + } + + // Check error exist in file + if (!empty($errorFiles)) + { + $this->setError($errorFiles); + } + } + + return $uploadedMediaId; + } + + /** + * Method to delete media record + * + * @param Integer $mediaId media Id of files table + * @param STRING $deletePath file path from params in config + * @param STRING $client client(example -'com_timelog.activity') + * @param Integer $clientId clientId(example - Timelog activity id) + * + * @return boolean True if successful, false if an error occurs. + * + * @since __DEPLOY_VERSION__ + */ + public function deleteMedia($mediaId, $deletePath, $client, $clientId) + { + JLoader::import("/techjoomla/media/tables/xref", JPATH_LIBRARIES); + JLoader::import("/techjoomla/media/tables/files", JPATH_LIBRARIES); + $tableXref = Table::getInstance('Xref', 'TJMediaTable'); + $filetable = Table::getInstance('Files', 'TJMediaTable'); + + // CheckMediaDataExist will return 1 when media is present clientId is Report Id + $checkMediaDataExist = $tableXref->load(array('media_id' => $mediaId, 'client_id' => $clientId)); + + // Making file delete path + $mediaPresent = $filetable->load($mediaId); + + $mediaType = explode(".", $filetable->type); + $deletePath = $deletePath . '/' . $mediaType[0]; + + // If Media is present + if ($checkMediaDataExist) + { + // Get Object which include Media xref + Media File data of provided Media xref id + $mediaXrefLib = TJMediaXref::getInstance(array('id' => $tableXref->id)); + + // If media is not deleted it will return false here + if ($mediaXrefLib->delete()) + { + // If media xref delete then delete main entry from media_files + $mediaLib = TJMediaStorageLocal::getInstance(array('id' => $mediaId, 'uploadPath' => $deletePath)); + + // Checking Media is present or not + if ($mediaLib->id) + { + // If Media is not deleted + if (!$mediaLib->delete()) + { + return false; + } + } + + return true; + } + else + { + return false; + } + } + elseif ($mediaPresent) + { + $mediaLib = TJMediaStorageLocal::getInstance(array('id' => $mediaId, 'uploadPath' => $deletePath)); + + if ($mediaLib->id) + { + if ($mediaLib->delete()) + { + return true; + } + else + { + return false; + } + } + } + + return true; + } + + /** + * Method to validate the form data. + * + * @param \JForm $form The form to validate against. + * @param Array $data The data to validate. + * + * @return array|boolean Array of filtered data if valid, false otherwise. + * + * @since __DEPLOY_VERSION__ + */ + public function validate($form, $data) + { + $return = true; + $return = parent::validate($form, $data); + + if (!empty($data['expired_on']) && $data['expired_on'] != '0000-00-00 00:00:00') + { + if ($data['issued_on'] > $data['expired_on']) + { + $this->setError(Text::_('COM_TJCERTIFICATE_EXPIRY_DATE_VALIDATION_MESSAGE')); + $return = false; + } + } + + return $return; + } +} diff --git a/src/components/com_tjcertificate/administrator/sql/install.mysql.utf8.sql b/src/components/com_tjcertificate/administrator/sql/install.mysql.utf8.sql index d5b51fee..e50305b3 100644 --- a/src/components/com_tjcertificate/administrator/sql/install.mysql.utf8.sql +++ b/src/components/com_tjcertificate/administrator/sql/install.mysql.utf8.sql @@ -33,6 +33,48 @@ CREATE TABLE IF NOT EXISTS `#__tj_certificate_issue` ( `state` tinyint(3) NOT NULL, `issued_on` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `expired_on` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `is_external` tinyint(1) NOT NULL DEFAULT '0', + `name` varchar(255) NOT NULL, + `issuing_org` varchar(255) NOT NULL, + `cert_url` text NULL, + `cert_file` varchar(255) NOT NULL, + `created_by` int(11) NOT NULL, + `access` tinyint(1) NOT NULL DEFAULT '0', + `status` varchar(64) NULL, PRIMARY KEY (`id`), UNIQUE KEY unqk_certificate_id (`unique_certificate_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `#__tj_media_files` +-- + +CREATE TABLE IF NOT EXISTS `#__tj_media_files` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(250) NOT NULL, + `type` varchar(250) NOT NULL, + `path` varchar(250) COLLATE utf8mb4_bin NOT NULL, + `state` tinyint(1) NOT NULL, + `source` varchar(250) NOT NULL, + `original_filename` varchar(250) COLLATE utf8mb4_bin NOT NULL, + `size` int(11) NOT NULL, + `storage` varchar(250) NOT NULL, + `created_by` int(11) NOT NULL, + `access` tinyint(1) NOT NULL, + `created_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `params` varchar(500) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1; + +-- +-- Table structure for table `#__tj_media_files_xref` +-- + +CREATE TABLE IF NOT EXISTS `#__tj_media_files_xref` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `media_id` int(11) NOT NULL, + `client_id` int(11) NOT NULL, + `client` varchar(250) NOT NULL, + `is_gallery` tinyint(1) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1; diff --git a/src/components/com_tjcertificate/administrator/sql/updates/mysql/1.0.3.sql b/src/components/com_tjcertificate/administrator/sql/updates/mysql/1.0.3.sql new file mode 100644 index 00000000..de1b71f4 --- /dev/null +++ b/src/components/com_tjcertificate/administrator/sql/updates/mysql/1.0.3.sql @@ -0,0 +1,42 @@ +ALTER TABLE `#__tj_certificate_issue` ADD COLUMN `is_external` tinyint(1) NOT NULL DEFAULT '0' AFTER `expired_on`; +ALTER TABLE `#__tj_certificate_issue` ADD COLUMN `name` varchar(255) NOT NULL AFTER `is_external`; +ALTER TABLE `#__tj_certificate_issue` ADD COLUMN `issuing_org` varchar(255) NOT NULL AFTER `name`; +ALTER TABLE `#__tj_certificate_issue` ADD COLUMN `cert_url` text NULL AFTER `issuing_org`; +ALTER TABLE `#__tj_certificate_issue` ADD COLUMN `cert_file` varchar(255) NOT NULL AFTER `cert_url`; +ALTER TABLE `#__tj_certificate_issue` ADD COLUMN `access` tinyint(1) NOT NULL DEFAULT '0' AFTER `cert_file`; +ALTER TABLE `#__tj_certificate_issue` ADD COLUMN `created_by` int(11) NOT NULL AFTER `access`; +ALTER TABLE `#__tj_certificate_issue` ADD COLUMN `status` varchar(64) NULL AFTER `created_by`; + +-- +-- Table structure for table `#__tj_media_files` +-- + +CREATE TABLE IF NOT EXISTS `#__tj_media_files` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(250) NOT NULL, + `type` varchar(250) NOT NULL, + `path` varchar(250) COLLATE utf8mb4_bin NOT NULL, + `state` tinyint(1) NOT NULL, + `source` varchar(250) NOT NULL, + `original_filename` varchar(250) COLLATE utf8mb4_bin NOT NULL, + `size` int(11) NOT NULL, + `storage` varchar(250) NOT NULL, + `created_by` int(11) NOT NULL, + `access` tinyint(1) NOT NULL, + `created_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `params` varchar(500) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1; + +-- +-- Table structure for table `#__tj_media_files_xref` +-- + +CREATE TABLE IF NOT EXISTS `#__tj_media_files_xref` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `media_id` int(11) NOT NULL, + `client_id` int(11) NOT NULL, + `client` varchar(250) NOT NULL, + `is_gallery` tinyint(1) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1; diff --git a/src/components/com_tjcertificate/administrator/tjcertificate.php b/src/components/com_tjcertificate/administrator/tjcertificate.php index 202eae8c..781effbd 100644 --- a/src/components/com_tjcertificate/administrator/tjcertificate.php +++ b/src/components/com_tjcertificate/administrator/tjcertificate.php @@ -15,6 +15,7 @@ use Joomla\CMS\MVC\Controller\BaseController; JLoader::import('components.com_tjcertificate.includes.tjcertificate', JPATH_ADMINISTRATOR); +TJCERT::init('admin'); JLoader::registerPrefix('TjCertificate', JPATH_ADMINISTRATOR); JLoader::register('TjCertificateController', JPATH_ADMINISTRATOR . '/controller.php'); diff --git a/src/components/com_tjcertificate/administrator/tjcertificateTemplate.json b/src/components/com_tjcertificate/administrator/tjcertificateTemplate.json new file mode 100644 index 00000000..dce916ef --- /dev/null +++ b/src/components/com_tjcertificate/administrator/tjcertificateTemplate.json @@ -0,0 +1,158 @@ +{ + "template1": { + "id": "", + "client": "com_tjcertificate", + "key": "assignRecordMailToUser", + "title": "Learning Record Added by Admin", + "user_control": 1, + "state": 1, + "core": 1, + "email":{ + "state": 1, + "cc": "", + "bcc": "", + "from_name": "", + "from_email": "", + "emailfields": { + "emailfields0": { + "id": "", + "language": "*", + "subject": "A New Learning Record \"{{record.name}}\" has been added against you", + "body": "

Dear {{user.name}},

A new record {{record.name}} added by {{assigner.name}}.

" + } + } + }, + "replacement_tags":[ + { + "name": "user.name", + "description": "Name of record owner" + }, + { + "name": "info.sitename", + "description": "Sitename" + }, + { + "name": "assigner.name", + "description": "Record assigner name" + }, + { + "name": "record.name", + "description": "Name of record" + } + ] + }, + "template2": { + "id": "", + "client": "com_tjcertificate", + "key": "createRecordMailTouser", + "title": "User has Submitted a Learning Record", + "user_control": 1, + "state": 1, + "core": 1, + "email":{ + "state": 1, + "cc": "", + "bcc": "", + "from_name": "", + "from_email": "", + "emailfields": { + "emailfields0": { + "id": "", + "language": "*", + "subject": "Record {{record.name}} added by you", + "body": "

Dear {{user.name}},

You have added record {{record.name}}.

" + } + } + }, + "replacement_tags":[ + { + "name": "user.name", + "description": "Name of record owner" + }, + { + "name": "info.sitename", + "description": "Sitename" + }, + { + "name": "record.name", + "description": "Name of record" + } + ] + }, + "template3": { + "id": "", + "client": "com_tjcertificate", + "key": "createRecordMailToAdmin", + "title": "Admin Notification when user Submits a Learning Record", + "user_control": 1, + "state": 1, + "core": 1, + "email":{ + "state": 1, + "cc": "", + "bcc": "", + "from_name": "", + "from_email": "", + "emailfields": { + "emailfields0": { + "id": "", + "language": "*", + "subject": "A new record {{record.name}} added", + "body": "

Hello Admin,

A new Record : {{record.name}} was added by {{user.name}} on {{info.sitename}}.

" + } + } + }, + "replacement_tags":[ + { + "name": "user.name", + "description": "Name of record owner" + }, + { + "name": "info.sitename", + "description": "Sitename" + }, + { + "name": "record.name", + "description": "Name of record" + } + ] + }, + "template4": { + "id": "", + "client": "com_tjcertificate", + "key": "recordPublishMailToUser", + "title": "Learning Record Approval Email Sent to user", + "user_control": 1, + "state": 1, + "core": 1, + "email":{ + "state": 1, + "cc": "", + "bcc": "", + "from_name": "", + "from_email": "", + "emailfields": { + "emailfields0": { + "id": "", + "language": "*", + "subject": "Record {{record.name}} is approved", + "body": "

Dear {{user.name}},

Your record {{record.name}} is approved.

" + } + } + }, + "replacement_tags":[ + { + "name": "user.name", + "description": "Name of record owner" + }, + { + "name": "info.sitename", + "description": "Sitename" + }, + { + "name": "record.name", + "description": "Name of record" + } + ] + } +} diff --git a/src/components/com_tjcertificate/administrator/views/certificate/tmpl/edit.php b/src/components/com_tjcertificate/administrator/views/certificate/tmpl/edit.php index 8dd9eeb3..69ae886a 100644 --- a/src/components/com_tjcertificate/administrator/views/certificate/tmpl/edit.php +++ b/src/components/com_tjcertificate/administrator/views/certificate/tmpl/edit.php @@ -15,16 +15,41 @@ use Joomla\CMS\Language\Text; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Router\Route; +use Joomla\CMS\Layout\LayoutHelper; HTMLHelper::addIncludePath(JPATH_COMPONENT . '/helpers/html'); +HTMLHelper::_('jquery.token'); +HTMLHelper::_('behavior.framework'); HTMLHelper::_('behavior.formvalidator'); HTMLHelper::_('behavior.keepalive'); HTMLHelper::_('formbehavior.chosen', 'select'); + +$options['relative'] = true; +HTMLHelper::_('script', 'com_tjcertificate/tjCertificateService.min.js', $options); +HTMLHelper::_('script', 'com_tjcertificate/template.min.js', $options); + +$app = Factory::getApplication(); +$input = $app->input; + +$client = $input->getCmd('client', ''); +$extension = $input->getCmd('extension', ''); + +$clientUrlAppend = ''; + +if (!empty($extension)) +{ + $clientUrlAppend = '&extension=' . $extension; +} +elseif (!empty($client)) +{ + $clientUrlAppend = '&client=' . $client; +} + ?>
-
sidebar)) { @@ -63,3 +88,19 @@
+ + + diff --git a/src/components/com_tjcertificate/administrator/views/certificate/view.html.php b/src/components/com_tjcertificate/administrator/views/certificate/view.html.php index 887236cd..fc3a93f9 100644 --- a/src/components/com_tjcertificate/administrator/views/certificate/view.html.php +++ b/src/components/com_tjcertificate/administrator/views/certificate/view.html.php @@ -14,6 +14,7 @@ use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\View\HtmlView; +use Joomla\CMS\Router\Route; /** * View to edit @@ -68,21 +69,24 @@ class TjCertificateViewCertificate extends HtmlView */ public function display($tpl = null) { + $app = Factory::getApplication(); $this->state = $this->get('State'); $this->item = $this->get('Item'); + + // If training record then redirect to training record form + if ($this->item->is_external) + { + $app->redirect( + Route::_('index.php?option=com_tjcertificate&view=trainingrecord&layout=edit&id=' . $this->item->id, false) + ); + } + $this->form = $this->get('Form'); $this->input = Factory::getApplication()->input; $this->canDo = JHelperContent::getActions('com_tjcertificate', 'certificate', $this->item->id); $layout = $this->input->get('layout', 'edit'); - if ($layout == 'edit') - { - JError::raiseNotice(403, Text::_('COM_TJCERTIFICATE_ERROR_CERTIFICATE_EDIT_NOT_PERMITTED')); - - return false; - } - // Check for errors. if (count($errors = $this->get('Errors'))) { @@ -143,7 +147,6 @@ protected function addToolbar() JToolbarHelper::apply('certificate.apply'); JToolbarHelper::save('certificate.save'); JToolbarHelper::save2new('certificate.save2new'); - JToolbarHelper::cancel('certificate.cancel'); } else { @@ -151,6 +154,16 @@ protected function addToolbar() // Can't save the record if it's checked out and editable $this->canSave($itemEditable); + } + + JToolbarHelper::modal('templatePreview', 'icon-eye', 'COM_TJCERTIFICATE_CERTIFICATE_TEMPLATE_TOOLBAR_PREVIEW'); + + if (empty($this->item->id)) + { + JToolbarHelper::cancel('certificate.cancel'); + } + else + { JToolbarHelper::cancel('certificate.cancel', 'JTOOLBAR_CLOSE'); } } diff --git a/src/components/com_tjcertificate/administrator/views/certificates/tmpl/default.php b/src/components/com_tjcertificate/administrator/views/certificates/tmpl/default.php index 4113aa13..3fabe596 100644 --- a/src/components/com_tjcertificate/administrator/views/certificates/tmpl/default.php +++ b/src/components/com_tjcertificate/administrator/views/certificates/tmpl/default.php @@ -30,15 +30,8 @@ $listOrder = $this->escape($this->state->get('list.ordering')); $listDirn = $this->escape($this->state->get('list.direction')); -$saveOrder = $listOrder == 'ci.id'; $dispatcher = JDispatcher::getInstance(); PluginHelper::importPlugin('content'); - -if ( $saveOrder ) -{ - $saveOrderingUrl = 'index.php?option=com_tjcertificate&task=certificates.saveOrderAjax'; - HTMLHelper::_('sortablelist.sortable', 'certificateList', 'adminForm', strtolower($listDirn), $saveOrderingUrl); -} ?>
@@ -87,7 +80,6 @@ - @@ -107,13 +99,12 @@ - + - + - @@ -131,6 +122,7 @@ items as $i => $item) { + $certificateObj = TJCERT::Certificate($item->id); $data = $dispatcher->trigger('getCertificateClientData', array($item->client_id, $item->client)); $item->max_ordering = 0; @@ -153,16 +145,15 @@
- + ?> + escape($item->unique_certificate_id); ?> + } + else + { + ?> escape($item->unique_certificate_id); ?> title)) ? $data[0]->title : '-'; + if ($item->is_external) + { + echo $item->name; + } + else + { + echo ($data[0]->title ? $data[0]->title : "-"); + } ?> - issued_on, Text::_('DATE_FORMAT_LC')); ?> + getFormatedDate($item->issued_on); ?> expired_on) && $item->expired_on != '0000-00-00 00:00:00') { - echo HTMLHelper::date($item->expired_on, Text::_('DATE_FORMAT_LC')); + echo $certificateObj->getFormatedDate($item->expired_on); } else { @@ -210,16 +208,23 @@ echo TEXT::_($client); ?> - escape($item->title); ?> toSql(); - + $link = ""; if ($item->expired_on > $utcNow || $item->expired_on == '0000-00-00 00:00:00') { // Get TJcertificate url for display certificate - $urlOpts = array ('absolute' => ''); - $link = TJCERT::Certificate($item->id)->getUrl($urlOpts, false); + $urlOpts = array ('absolute' => true); + + if ($item->is_external) + { + $link = $certificateObj->getUrl($urlOpts, false, true); + } + else + { + $link = $certificateObj->getUrl($urlOpts, false); + } ?>
- escape($item->comment); ?> + + + + + + + + id; ?> canDo; - /*if ($canDo->get('core.create')) + if ($canDo->get('core.create')) { JToolbarHelper::addNew('certificate.add'); } + if ($canDo->get('certificate.external.create')) + { + JToolbarHelper::addNew('trainingrecord.add', 'COM_TJCERTIFICATE_ADD_EXTERNAL_CERTIFICATE'); + } + if ($canDo->get('core.edit')) { JToolbarHelper::editList('certificate.edit'); @@ -156,7 +161,6 @@ protected function addToolbar() JToolbarHelper::deleteList('JGLOBAL_CONFIRM_DELETE', 'certificates.delete', 'JTOOLBAR_DELETE'); JToolbarHelper::divider(); } - */ if ($canDo->get('core.admin') || $canDo->get('core.options')) { diff --git a/src/components/com_tjcertificate/administrator/views/template/tmpl/edit.php b/src/components/com_tjcertificate/administrator/views/template/tmpl/edit.php index 8de3209f..fee116ad 100644 --- a/src/components/com_tjcertificate/administrator/views/template/tmpl/edit.php +++ b/src/components/com_tjcertificate/administrator/views/template/tmpl/edit.php @@ -141,39 +141,19 @@
- - - - + diff --git a/src/components/com_tjcertificate/administrator/views/templates/tmpl/default.php b/src/components/com_tjcertificate/administrator/views/templates/tmpl/default.php index 4d26991d..1a1b6525 100644 --- a/src/components/com_tjcertificate/administrator/views/templates/tmpl/default.php +++ b/src/components/com_tjcertificate/administrator/views/templates/tmpl/default.php @@ -100,7 +100,7 @@ - + @@ -201,7 +201,13 @@
- escape($item->client); ?> + + client); + $client = strtoupper("COM_TJCERTIFICATE_CLIENT_" . $client); + echo TEXT::_($client); + ?> + is_public); ?> escape($item->uname); ?> id; ?> diff --git a/src/components/com_tjcertificate/administrator/views/trainingrecord/index.html b/src/components/com_tjcertificate/administrator/views/trainingrecord/index.html new file mode 100644 index 00000000..e69de29b diff --git a/src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/edit.php b/src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/edit.php new file mode 100644 index 00000000..0ffc182f --- /dev/null +++ b/src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/edit.php @@ -0,0 +1,111 @@ + + * @copyright Copyright (C) 2009 - 2020 Techjoomla. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Layout\LayoutHelper; +use Joomla\CMS\Uri\Uri; + +HTMLHelper::addIncludePath(JPATH_COMPONENT . '/helpers/html'); + +HTMLHelper::_('jquery.token'); +HTMLHelper::_('behavior.framework'); +HTMLHelper::_('behavior.formvalidator'); +HTMLHelper::_('behavior.keepalive'); +HTMLHelper::_('formbehavior.chosen', 'select'); + +$options['relative'] = true; +HTMLHelper::_('script', 'com_tjcertificate/tjCertificateService.min.js', $options); +HTMLHelper::_('script', 'com_tjcertificate/certificate.min.js', $options); +?> +
+
+
+ sidebar)) + { + ?> +
+ sidebar; ?> +
+
+ +
+ +
+ + +
+ form->renderField('id'); ?> + form->renderField('assigned_user_id'); ?> + form->renderField('name'); ?> + form->renderField('unique_certificate_id'); ?> + form->renderField('cert_url'); ?> + form->renderField('issuing_org'); ?> + form->renderField('issued_on'); ?> + form->renderField('expired_on'); ?> + form->renderField('status'); ?> +
+
form->getLabel('cert_file'); ?>
+
+ form->getInput('cert_file'); ?> + item->mediaData[0]) + { + $downloadAttachmentLink = Uri::root() . 'index.php?option=com_tjcertificate&task=trainingrecord.downloadAttachment&id=' . $this->item->mediaData[0]->media_id . '&recordId=' . $this->item->id; + echo ''; + ?> + + item->mediaData[0]->title;?> + + + + + + +
+
+ form->renderField('state'); ?> + form->renderField('comment'); ?> + +
+ + + +
+ +
+
+ + diff --git a/src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/index.html b/src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/index.html new file mode 100644 index 00000000..e69de29b diff --git a/src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/preview.php b/src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/preview.php new file mode 100644 index 00000000..52d5fe3c --- /dev/null +++ b/src/components/com_tjcertificate/administrator/views/trainingrecord/tmpl/preview.php @@ -0,0 +1,95 @@ + + * @copyright Copyright (C) 2009 - 2020 Techjoomla. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); +use Joomla\CMS\Language\Text; +use Joomla\CMS\Uri\Uri; +?> +
+ + + +
+
+ +
+
+ escape($this->item->name); ?> +
+ item->cert_url) { ?> +
+ +
+
+ escape($this->item->cert_url); ?> +
+ +
+ +
+
+ escape($this->item->issuing_org); ?> +
+
+ +
+
+ escape($this->item->status)); ?> +
+
+ +
+
+ certificate->getFormatedDate($this->item->issued_on);?> +
+ item->expired_on != "0000-00-00 00:00:00") { ?> +
+ +
+
+ certificate->getFormatedDate($this->item->expired_on);?> +
+ + item->cert_file) { ?> +
+
+
+ item->mediaData[0]) + { + if ($this->item->mediaData[0]->type === "image") + { + ?> + + + + item->mediaData[0]->title;?> + + + +
+ + item->comment) { ?> +
+ +
+
+ escape($this->item->comment); ?> +
+ +
+
diff --git a/src/components/com_tjcertificate/administrator/views/trainingrecord/view.html.php b/src/components/com_tjcertificate/administrator/views/trainingrecord/view.html.php new file mode 100644 index 00000000..b9bba39a --- /dev/null +++ b/src/components/com_tjcertificate/administrator/views/trainingrecord/view.html.php @@ -0,0 +1,182 @@ + + * @copyright Copyright (C) 2009 - 2020 Techjoomla. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\CMS\MVC\View\HtmlView; +use Joomla\CMS\Component\ComponentHelper; + +/** + * View to edit + * + * @since __DEPLOY_VERSION__ + */ +class TjCertificateViewTrainingRecord extends HtmlView +{ + /** + * The JForm object + * + * @var JForm + */ + protected $form; + + /** + * The active item + * + * @var object + */ + protected $item; + + /** + * The model state + * + * @var object + */ + protected $state; + + /** + * The actions the user is authorised to perform + * + * @var JObject + */ + protected $canDo; + + /** + * Display the view + * + * @param string $tpl Template name + * + * @return void + * + * @throws Exception + */ + public function display($tpl = null) + { + $this->state = $this->get('State'); + $this->item = $this->get('Item'); + $this->form = $this->get('Form'); + $this->input = Factory::getApplication()->input; + $this->canDo = JHelperContent::getActions('com_tjcertificate', 'certificate', $this->item->id); + $this->params = ComponentHelper::getParams('com_tjcertificate'); + $this->allowedFileExtensions = $this->params->get('upload_extensions'); + $this->uploadLimit = $this->params->get('upload_maxsize', '1024'); + $this->certificate = TJCERT::Certificate(); + + $layout = $this->input->get('layout', 'edit'); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + $this->addToolbar(); + + parent::display($tpl); + } + + /** + * Add the page title and toolbar. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + protected function addToolbar() + { + $user = Factory::getUser(); + $userId = $user->id; + $isNew = empty($this->item->id); + + // Built the actions for new and existing records. + $canDo = $this->canDo; + $layout = Factory::getApplication()->input->get("layout"); + + $app = Factory::getApplication(); + + JLoader::import('administrator.components.com_tjcertificate.helpers.tjcertificate', JPATH_SITE); + TjCertificateHelper::addSubmenu('certificates'); + + if ($app->isAdmin()) + { + $this->sidebar = JHtmlSidebar::render(); + } + + // For new records, check the create permission. + if ($layout != "default") + { + Factory::getApplication()->input->set('hidemainmenu', true); + + JToolbarHelper::title( + Text::_('COM_TJCERTIFICATE_PAGE_' . ($isNew ? 'ADD_TRAINING_RECORD' : 'EDIT_TRAINING_RECORD')), + 'pencil-2 certificate-add' + ); + + if ($isNew) + { + JToolbarHelper::apply('trainingrecord.apply'); + JToolbarHelper::save('trainingrecord.save'); + JToolbarHelper::save2new('trainingrecord.save2new'); + } + else + { + $itemEditable = $this->isEditable($canDo, $userId); + + // Can't save the record if it's checked out and editable + $this->canSave($itemEditable); + } + + if (empty($this->item->id)) + { + JToolbarHelper::cancel('certificate.cancel'); + } + else + { + JToolbarHelper::cancel('certificate.cancel', 'JTOOLBAR_CLOSE'); + } + } + + JToolbarHelper::divider(); + } + + /** + * Can't save the record if it's checked out and editable + * + * @param boolean $itemEditable Item editable + * + * @return void + */ + protected function canSave($itemEditable) + { + if ($itemEditable) + { + JToolbarHelper::apply('trainingrecord.apply'); + JToolbarHelper::save('trainingrecord.save'); + } + } + + /** + * Is editable + * + * @param Object $canDo Checked Out + * + * @param integer $userId User ID + * + * @return boolean + */ + protected function isEditable($canDo, $userId) + { + // Since it's an existing record, check the edit permission, or fall back to edit own if the owner. + return $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by == $userId); + } +} diff --git a/src/components/com_tjcertificate/media/images/buttons/en_US.png b/src/components/com_tjcertificate/media/images/buttons/en_US.png new file mode 100644 index 0000000000000000000000000000000000000000..22fdb3a4f72c782963fea6e03f999db5bf06d83f GIT binary patch literal 139290 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 Qo~^s8*n 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 + * @copyright Copyright (C) 2009 - 2020 Techjoomla. All rights reserved. + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL + */ + +var certificate = { + validateFile: function(thisFile) { + /** Validation is for file field only */ + if (jQuery(thisFile).attr('type') != 'file') { + return false; + } + + /** Clear error message */ + jQuery('#system-message-container').empty(); + + var uploadedfile = jQuery(thisFile)[0].files[0]; + var fileType = uploadedfile.type; + var fileExtension = uploadedfile.name.split("."); + + /** global: allowedAttachments */ + var allowedExtensionsArray = allowedAttachments.split(","); + + var invalid = 0; + var errorMsg = new Array(); + + if ((fileExtension[fileExtension.length - 1] !== '' || fileExtension[fileExtension.length - 1] !== null) && (jQuery.inArray(fileType, allowedExtensionsArray) == -1)) { + invalid = "1"; + errorMsg.push(Joomla.JText._('COM_TJCERTIFICATE_MEDIA_INVALID_FILE_TYPE')); + } + + var uploadedFileSize = uploadedfile.size; + + + /** global: attachmentMaxSize */ + if (uploadedFileSize > attachmentMaxSize * 1024 * 1024) { + + invalid = "1"; + errorMsg.push(Joomla.JText._('COM_TJCERTIFICATE_MEDIA_UPLOAD_ERROR')); + console.log("COM_TIMELOG_FILE_SIZE_ERROR"); + } + + if (invalid) { + Joomla.renderMessages({ + 'error': errorMsg + }); + + jQuery("html, body").animate({ + scrollTop: 0 + }, 500); + + return false; + } + }, + deleteAttachment: function(currentElement) { + if (confirm(Joomla.JText._('COM_TJCERTIFICATE_CONFIRM_DELETE_ATTACHMENT')) == true) { + var formData = {}; + + if (currentElement == '' || currentElement === undefined) { + return false; + } + + formData['certificateId'] = jQuery(currentElement).attr('data-aid'); + formData['mediaId'] = jQuery(currentElement).attr('data-mid'); + + var promise = tjCertificateService.deleteAttachment(formData); + + promise.fail( + function(response) { + var messages = { + "error": [response.responseText] + }; + certificate.renderMessage(messages); + } + ).done(function(response) { + + if (!response.success && response.message) { + var messages = { + "error": [response.message] + }; + certificate.renderMessage(messages); + } + + if (response.messages) { + certificate.renderMessage(response.messages); + } + + if (response.success) { + certificate.renderMessage(response.message); + } + + jQuery(currentElement).closest("span.fileupload").remove(); + }); + } + }, + deleteItem: function(certificateId, obj) { + if (confirm(Joomla.JText._('COM_TJCERTIFICATE_DELETE_CERTIFICATE_MESSAGE')) == true) { + var formData = {}; + + if (certificateId == '' || certificateId === undefined) { + return false; + } + + formData['certificateId'] = certificateId; + + var promise = tjCertificateService.deleteItem(formData); + + promise.fail( + function(response) { + var messages = { + "error": [response.responseText] + }; + certificate.renderMessage(messages); + } + ).done(function(response) { + + if (!response.success && response.message) { + var messages = { + "error": [response.message] + }; + certificate.renderMessage(messages); + } + + if (response.messages) { + certificate.renderMessage(response.messages); + } + + if (response.success) { + certificate.renderMessage(response.message); + } + + jQuery(obj).closest("tr").remove(); + }); + } + }, + renderMessage: function(msg) { + Joomla.renderMessages({ + 'alert alert-success': [msg] + }); + jQuery("html, body").animate({ + scrollTop: 0 + }, 2000); + }, + validationEndDate: function(expDateObj) { + var expDate = jQuery(expDateObj).val(); + var issueDate = jQuery('#jform_issued_on').val(); + + jQuery(document).ready(function(){ + document.formvalidator.setHandler('expdate', function (value) { + if (issueDate > expDate) { + certificate.renderMessage(Joomla.JText._('COM_TJCERTIFICATE_EXPIRY_DATE_VALIDATION_MESSAGE')); + jQuery('#jform_expired_on').val(""); + + return false; + } + + return true; + + }); + }); + } +}; diff --git a/src/components/com_tjcertificate/media/js/certificate.min.js b/src/components/com_tjcertificate/media/js/certificate.min.js new file mode 100644 index 00000000..1a871180 --- /dev/null +++ b/src/components/com_tjcertificate/media/js/certificate.min.js @@ -0,0 +1 @@ +var certificate={validateFile:function(e){if("file"!=jQuery(e).attr("type"))return!1;jQuery("#system-message-container").empty();var r=jQuery(e)[0].files[0],t=r.type,a=r.name.split("."),s=allowedAttachments.split(","),i=0,n=new Array;return""===a[a.length-1]&&null===a[a.length-1]||-1!=jQuery.inArray(t,s)||(i="1",n.push(Joomla.JText._("COM_TJCERTIFICATE_MEDIA_INVALID_FILE_TYPE"))),r.size>1024*attachmentMaxSize*1024&&(i="1",n.push(Joomla.JText._("COM_TJCERTIFICATE_MEDIA_UPLOAD_ERROR")),console.log("COM_TIMELOG_FILE_SIZE_ERROR")),i?(Joomla.renderMessages({error:n}),jQuery("html, body").animate({scrollTop:0},500),!1):void 0},deleteAttachment:function(e){if(1==confirm(Joomla.JText._("COM_TJCERTIFICATE_CONFIRM_DELETE_ATTACHMENT"))){var r={};if(""==e||void 0===e)return!1;r.certificateId=jQuery(e).attr("data-aid"),r.mediaId=jQuery(e).attr("data-mid"),tjCertificateService.deleteAttachment(r).fail(function(e){var r={error:[e.responseText]};certificate.renderMessage(r)}).done(function(r){if(!r.success&&r.message){var t={error:[r.message]};certificate.renderMessage(t)}r.messages&&certificate.renderMessage(r.messages),r.success&&certificate.renderMessage(r.message),jQuery(e).closest("span.fileupload").remove()})}},deleteItem:function(e,r){if(1==confirm(Joomla.JText._("COM_TJCERTIFICATE_DELETE_CERTIFICATE_MESSAGE"))){var t={};if(""==e||void 0===e)return!1;t.certificateId=e,tjCertificateService.deleteItem(t).fail(function(e){var r={error:[e.responseText]};certificate.renderMessage(r)}).done(function(e){if(!e.success&&e.message){var t={error:[e.message]};certificate.renderMessage(t)}e.messages&&certificate.renderMessage(e.messages),e.success&&certificate.renderMessage(e.message),jQuery(r).closest("tr").remove()})}},renderMessage:function(e){Joomla.renderMessages({"alert alert-success":[e]}),jQuery("html, body").animate({scrollTop:0},2e3)},validationEndDate:function(e){var r=jQuery(e).val(),t=jQuery("#jform_issued_on").val();jQuery(document).ready(function(){document.formvalidator.setHandler("expdate",function(e){return!(t>r)||(certificate.renderMessage(Joomla.JText._("COM_TJCERTIFICATE_EXPIRY_DATE_VALIDATION_MESSAGE")),jQuery("#jform_expired_on").val(""),!1)})})}}; diff --git a/src/components/com_tjcertificate/media/js/certificateImage.js b/src/components/com_tjcertificate/media/js/certificateImage.js index ce2977d9..71d10e80 100644 --- a/src/components/com_tjcertificate/media/js/certificateImage.js +++ b/src/components/com_tjcertificate/media/js/certificateImage.js @@ -47,6 +47,13 @@ var certificateImage = { img.src = imagePath + certificateId + ".png"; jQuery("#previewImage").append(img); setTimeout(function(){ + + if (screen.width < 1200) + { + viewport = document.querySelector("meta[name=viewport]"); + viewport.setAttribute("content", "width=device-width"); + } + Joomla.loadingLayer('hide'); }, 1000); } @@ -57,6 +64,13 @@ var certificateImage = { generateImage: function(element) { // jQuery('#certificateContent').width(element.offsetWidth).height(element.offsetHeight); + + if (screen.width < 1200) + { + viewport = document.querySelector("meta[name=viewport]"); + viewport.setAttribute("content", "width=1200px"); + } + Joomla.loadingLayer('show'); html2canvas(element, { diff --git a/src/components/com_tjcertificate/media/js/certificateImage.min.js b/src/components/com_tjcertificate/media/js/certificateImage.min.js index de5e7ab6..d677faa5 100644 --- a/src/components/com_tjcertificate/media/js/certificateImage.min.js +++ b/src/components/com_tjcertificate/media/js/certificateImage.min.js @@ -1 +1 @@ -var certificateImage={printCertificate:function(e){var t=document.getElementById(e).innerHTML,o=document.body.innerHTML;document.body.innerHTML=t,window.print(),document.body.innerHTML=o,certificateImage.enableDownloadShareBtns()},enableDownloadShareBtns:function(){jQuery("#download-popover").popover({trigger:"focus",html:!0,content:jQuery("#download-popover-content").html()}),jQuery("#sharing-popover").popover({trigger:"focus",html:!0,content:jQuery("#sharing-popover-content").html()}),jQuery("#copyurl").popover()},uploadImage:function(e){var t=!1,o=jQuery("#certificateId").val();return jQuery.ajax({url:certRootUrl+"index.php?option=com_tjcertificate&task=certificate.uploadCertificate",type:"POST",data:{image:e,certificateId:o},success:function(e){t=e;var o=jQuery("#certificateId").val(),n=certRootUrl+"media/com_tjcertificate/certificates/",r=document.createElement("img");jQuery("#certificateContent").hide(),r.src=n+o+".png",jQuery("#previewImage").append(r),setTimeout(function(){Joomla.loadingLayer("hide")},1e3)}}),t},generateImage:function(e){Joomla.loadingLayer("show"),html2canvas(e,{scrollX:0,scrollY:-window.scrollY,allowTaint:!0}).then(function(e){certificateImage.enableDownloadShareBtns(),certificateImage.uploadImage(e.toDataURL("image/png"))})},copyUrl:function(e){e="#"+e;var t=document.createElement("input"),o=jQuery(e).attr("data-alt-url");jQuery(e).popover("show"),document.body.appendChild(t),t.value=o,t.select(),document.execCommand("copy"),document.body.removeChild(t),setTimeout(function(){jQuery(e).popover("hide")},1e3)}}; \ No newline at end of file +var certificateImage={printCertificate:function(e){var t=document.getElementById(e).innerHTML,o=document.body.innerHTML;document.body.innerHTML=t,window.print(),document.body.innerHTML=o,certificateImage.enableDownloadShareBtns()},enableDownloadShareBtns:function(){jQuery("#download-popover").popover({trigger:"focus",html:!0,content:jQuery("#download-popover-content").html()}),jQuery("#sharing-popover").popover({trigger:"focus",html:!0,content:jQuery("#sharing-popover-content").html()}),jQuery("#copyurl").popover()},uploadImage:function(e){var t=!1,o=jQuery("#certificateId").val();return jQuery.ajax({url:certRootUrl+"index.php?option=com_tjcertificate&task=certificate.uploadCertificate",type:"POST",data:{image:e,certificateId:o},success:function(e){t=e;var o=jQuery("#certificateId").val(),r=certRootUrl+"media/com_tjcertificate/certificates/",n=document.createElement("img");jQuery("#certificateContent").hide(),n.src=r+o+".png",jQuery("#previewImage").append(n),setTimeout(function(){screen.width<1200&&(viewport=document.querySelector("meta[name=viewport]"),viewport.setAttribute("content","width=device-width")),Joomla.loadingLayer("hide")},1e3)}}),t},generateImage:function(e){screen.width<1200&&(viewport=document.querySelector("meta[name=viewport]"),viewport.setAttribute("content","width=1200px")),Joomla.loadingLayer("show"),html2canvas(e,{scrollX:0,scrollY:-window.scrollY,allowTaint:!0}).then(function(e){certificateImage.enableDownloadShareBtns(),certificateImage.uploadImage(e.toDataURL("image/png"))})},copyUrl:function(e){e="#"+e;var t=document.createElement("input"),o=jQuery(e).attr("data-alt-url");jQuery(e).popover("show"),document.body.appendChild(t),t.value=o,t.select(),document.execCommand("copy"),document.body.removeChild(t),setTimeout(function(){jQuery(e).popover("hide")},1e3)}}; \ No newline at end of file diff --git a/src/components/com_tjcertificate/media/js/template.js b/src/components/com_tjcertificate/media/js/template.js index ab980df2..60756439 100644 --- a/src/components/com_tjcertificate/media/js/template.js +++ b/src/components/com_tjcertificate/media/js/template.js @@ -9,19 +9,20 @@ var template = { - previewTemplate: function () { + previewTemplate: function (id) { jQuery(document).on('click', 'button[data-target="#templatePreview"]', function () { - + jQuery('#show-info').hide(); + var editorId = jQuery('#'+id); if (typeof tinyMCE != "undefined") { - tinyMCE.execCommand('mceToggleEditor', false, 'jform_body'); + tinyMCE.execCommand('mceToggleEditor', false, id); } else if (typeof CodeMirror != "undefined") { var editor = document.querySelector('.CodeMirror').CodeMirror; - jQuery('#jform_body').html(editor.getValue()); + editorId.html(editor.getValue()); } else { @@ -30,14 +31,14 @@ var template = { jQuery('#previewTempl').empty(); jQuery('