diff --git a/actions/generate.php b/actions/generate.php new file mode 100644 index 0000000..2d6fed2 --- /dev/null +++ b/actions/generate.php @@ -0,0 +1,56 @@ + + * @copyright (C) Engr. Arsalan Shah + * @license Open Source Social Network License (OSSN LICENSE) http://www.opensource-socialnetwork.org/licence + * @link https://www.opensource-socialnetwork.org/ + */ +$sw = input('service_worker'); + +if($sw == 'enabled') { + //copied from original sw + $siteurl = ossn_site_url(); + if(substr($siteurl, 0, 8) != 'https://') { + ossn_trigger_message(ossn_print('favicon:generator:sw:https:error'), 'error'); + redirect(REF); + } +} +$logo = new OssnFile(); +$logo->setFile('logo'); +$logo->setExtension(array( + 'jpg', + 'png', + 'jpeg', + 'jfif', + 'gif', + 'webp', +)); +if(isset($logo->file['tmp_name']) && $logo->typeAllowed()) { + $file = $logo->file['tmp_name']; + + $info = getimagesize($file); + $width = $info[0]; + $height = $info[1]; + + if($width != $height) { + ossn_trigger_message(ossn_print('favicon:generator:logo:dim:error'), 'error'); + redirect(REF); + } + + $time = time(); + + $icon = new Favicon\Generator($file, $time); + if($sw == 'enabled') { + $icon->serviceworker = true; + } + $icon->generate(); + + ossn_trigger_message(ossn_print('favicon:generator:logo:done')); + redirect(REF); +} else { + ossn_trigger_message(ossn_print('favicon:generator:logo:error'), 'error'); + redirect(REF); +} \ No newline at end of file diff --git a/actions/reset.php b/actions/reset.php new file mode 100644 index 0000000..e9cbbb6 --- /dev/null +++ b/actions/reset.php @@ -0,0 +1,13 @@ + + * @copyright (C) Engr. Arsalan Shah + * @license Open Source Social Network License (OSSN LICENSE) http://www.opensource-socialnetwork.org/licence + * @link https://www.opensource-socialnetwork.org/ + */ + $path = ossn_route()->www . 'sfavicons/'; + \OssnFile::DeleteDir($path); + redirect(REF); \ No newline at end of file diff --git a/classes/Generator.php b/classes/Generator.php new file mode 100644 index 0000000..7c3439f --- /dev/null +++ b/classes/Generator.php @@ -0,0 +1,188 @@ + + * @copyright (C) Engr. Arsalan Shah + * @license Open Source Social Network License (OSSN LICENSE) http://www.opensource-socialnetwork.org/licence + * @link https://www.opensource-socialnetwork.org/ + */ +namespace Favicon; +class Generator { + public function __construct($file, $time) { + $this->file = $file; + $this->time = $time; + $this->serviceworker = false; + } + public function browserXML() { + $file = __FaviconGenerator__ . 'configs/browserconfig.xml.dist'; + $file = file_get_contents($file); + + $urllogo_310 = ossn_site_url("sfavicons/favicon-310x310.png?v{$this->time}"); + $urllogo_512 = ossn_site_url("sfavicons/favicon-512x512.png?v{$this->time}"); + + $newfile = str_replace('{512logo_url}', $urllogo_512, $file); + $newfile = str_replace('{310logo_url}', $urllogo_310, $newfile); + return $newfile; + } + public function webManifest() { + $urllogo_192 = ossn_site_url("sfavicons/favicon-192x192.png?v{$this->time}"); + $urllogo_512 = ossn_site_url("sfavicons/favicon-512x512.png?v{$this->time}"); + + $sitename = ossn_site_settings('site_name'); + + $manifest = array( + 'name' => $sitename, + 'short_name' => $sitename, + 'icons' => array( + array( + 'src' => $urllogo_192, + 'sizes' => '192x192', + 'type' => 'image/png', + 'purpose' => 'any maskable', + ), + array( + 'src' => $urllogo_512, + 'sizes' => '512x512', + 'type' => 'image/png', + 'purpose' => 'any maskable', + ), + ), + + 'theme_color' => '#ffffff', + 'background_color' => '#ffffff', + 'start_url' => ossn_site_url(), + 'display' => 'standalone', + 'orientation' => 'any', + 'scope' => ossn_site_url(), + ); + if(isset($this->serviceworker) && $this->serviceworker === true) { + $manifest['serviceworker'] = array( + 'src' => ossn_site_url('sfavicons/sw.js'), + ); + } + return json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + } + public function html() { + return ossn_plugin_view('favicon_generator/html', array( + 'time' => $this->time, + )); + } + public function yandex() { + $manifest = array( + 'version' => '1.0', + 'api_version' => 1, + 'layout' => array( + 'logo' => ossn_site_url("sfavicons/favicon-50x50.png?v{$this->time}"), + 'color' => 'background_color', + 'show_title' => true, + ), + ); + return json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + } + public function ico($png32, $destination) { + if(!is_file($png32) || !is_dir($destination)) { + return false; + } + require_once __FaviconGenerator__ . 'vendors/png2ico/php2ico.php'; + $icon = new \php2ico($png32); + return $icon->save_ico($destination . 'favicon.ico'); + } + public function resize($input_name, $maxwidth, $maxheight) { + // Get the size information from the image + $imgsizearray = getimagesize($input_name); + if($imgsizearray == false) { + return false; + } + $image = new \OssnImage($input_name); + + $accepted_formats = array( + 'image/jpeg' => 'jpeg', + 'image/pjpeg' => 'jpeg', + 'image/png' => 'png', + 'image/x-png' => 'png', + 'image/webp' => 'webp', + ); + + // make sure the function is available + $load_function = 'imagecreatefrom' . $accepted_formats[$imgsizearray['mime']]; + if(!is_callable($load_function)) { + return false; + } + $image->resizeToBestFit($maxwidth, $maxheight); + return $image->getImageAsString(IMAGETYPE_PNG, 50); + } + public function generate() { + if(!is_file($this->file)) { + return false; + } + $path = ossn_route()->www . 'sfavicons/'; + + //delete old dir and create again + \OssnFile::DeleteDir($path); + mkdir($path, 0755, true); + + //png icon files + $config = array( + 16, + 32, + 37, + 48, + 50, + 57, + 60, + 72, + 76, + 96, + 114, + 120, + 144, + 152, + 180, + 192, + 150, + 310, + 512, + ); + foreach ($config as $size) { + $new = $this->resize($this->file, $size, $size, false); + file_put_contents($path . "favicon-{$size}x{$size}.png", $new); + } + + //ico file + //we need to use 32px PNG file + if(!$this->ico($path . 'favicon-32x32.png', $path)) { + //failed for some reason + //delete generated icons + error_log("FaviconGenerator: Failed to generate ICO file from {$path}"); + \OssnFile::DeleteDir($path); + return false; + } + + //webmanifest + $manifest = $this->webManifest(); + file_put_contents($path . 'manifest.json', $manifest); + + //yandex + $yandex = $this->yandex(); + file_put_contents($path . 'yandex-browser-manifest.json', $yandex); + + //browserxml + $browserxml = $this->browserXML(); + file_put_contents($path . 'browserconfig.xml', $browserxml); + + //header + $html = $this->html(); + file_put_contents($path . 'head.html', $html); + + //service worker + if(isset($this->serviceworker) && $this->serviceworker === true) { + $sw = ossn_plugin_view('favicon_generator/favicon-sw'); + file_put_contents($path . 'sw.js', $sw); + } + + //generated settings + file_put_contents($path . 'generated', 1); + } +} \ No newline at end of file diff --git a/configs/browserconfig.xml.dist b/configs/browserconfig.xml.dist new file mode 100644 index 0000000..3df87b2 --- /dev/null +++ b/configs/browserconfig.xml.dist @@ -0,0 +1,10 @@ + + + + + + + #2b5797 + + + diff --git a/locale/ossn.en.php b/locale/ossn.en.php new file mode 100644 index 0000000..643122a --- /dev/null +++ b/locale/ossn.en.php @@ -0,0 +1,25 @@ + + * @copyright (C) Engr. Arsalan Shah + * @license Open Source Social Network License (OSSN LICENSE) http://www.opensource-socialnetwork.org/licence + * @link https://www.opensource-socialnetwork.org/ + */ +ossn_register_languages('en', array( + 'favicons:generator:select' => 'Select', + 'favicons:generator:enabled' => 'Enabled', + 'favicons:generator:disabled' => 'Disabled', + 'favicon:generator:sw:https:error' => 'Enabling PWA / service Worker not possible - Your site must be HTTPS supported.', + 'favicon:generator:logo:dim:error' => 'Logo must be square and at least 512px * 512px', + 'favicon:generator:logo:info' => 'Logo File (at least 512px * 512px and square)', + 'favicon:generator:logo:info:2' => 'Make sure logo file is no more than 1MB', + 'favicon:generator:sw' => 'Enable Service Worker', + 'favicon:generator:sw:info' => 'When Enabled it will enable progressive web app (PWA). Allowing users to install PWA on supported device. Make sure you are using SSL protocol (https).', + 'favicon:generator:logo:error' => 'Invalid Logo file', + 'favicon:generator:logo:done' => 'Logo has been generated', + 'favicon:generator:reset' => 'Reset Now', + 'favicon:generator:reset:info' => 'Icons are already generated. You can reset to change the favicon for you website.', +)); \ No newline at end of file diff --git a/ossn_com.php b/ossn_com.php new file mode 100644 index 0000000..b51d6ce --- /dev/null +++ b/ossn_com.php @@ -0,0 +1,52 @@ + + * @copyright (C) Engr. Arsalan Shah + * @license Open Source Social Network License (OSSN LICENSE) http://www.opensource-socialnetwork.org/licence + * @link https://www.opensource-socialnetwork.org/ + */ + +define('__FaviconGenerator__', ossn_route()->com . 'FaviconGenerator/'); +ossn_register_class(array( + 'Favicon\Generator' => __FaviconGenerator__ . 'classes/Generator.php', +)); +ossn_register_callback('ossn', 'init', function () { + if(ossn_isAdminLoggedin()) { + ossn_register_com_panel('FaviconGenerator', 'settings'); + ossn_register_action('favicon_generator/settings', __FaviconGenerator__ . 'actions/generate.php'); + ossn_register_action('favicon_generator/reset', __FaviconGenerator__ . 'actions/reset.php'); + } + //taken from original favicons component + ossn_add_hook('page', 'override:view', '_com_favicons_page_handler'); + ossn_extend_view('ossn/admin/head', '_com_favicons_add_head_tags'); + + //from original service worker + ossn_extend_view('ossn/site/head', '_com_serviceworker_enable_service_worker'); + ossn_extend_view('ossn/admin/head', '_com_serviceworker_enable_service_worker'); +}); +function _com_serviceworker_enable_service_worker() { + $sw_file = ossn_route()->www . 'sfavicons/sw.js'; + $sw_url = ossn_site_url() . 'sfavicons/sw.js'; + if(file_exists($sw_file)) { + $script = + "\n\n"; + return $script; + } +} +function _com_favicons_page_handler($hook, $type, $return, $params) { + if($params['handler'] != 'shared_content') { + ossn_extend_view('ossn/site/head', '_com_favicons_add_head_tags'); + } +} + +function _com_favicons_add_head_tags() { + $tags_file = ossn_route()->www . 'sfavicons/head.html'; + if(file_exists($tags_file)) { + $content = file_get_contents($tags_file, true); + $tags = "\n" . $content . "\n"; + return $tags; + } +} \ No newline at end of file diff --git a/ossn_com.xml b/ossn_com.xml new file mode 100644 index 0000000..012ae28 --- /dev/null +++ b/ossn_com.xml @@ -0,0 +1,20 @@ + + + Favicon Generator + FaviconGenerator + Core Team + Favicon Generator + Open Source Social Network License (OSSN LICENSE) v4.0 + https://www.opensource-socialnetwork.org.com/ + http://www.opensource-socialnetwork.org/licence + 1.0 + + ossn_version + 7.6 + + + ossn_component + Favicons + disabled + + diff --git a/plugins/default/favicon_generator/favicon-sw-unregister.php b/plugins/default/favicon_generator/favicon-sw-unregister.php new file mode 100644 index 0000000..2a76c6b --- /dev/null +++ b/plugins/default/favicon_generator/favicon-sw-unregister.php @@ -0,0 +1,13 @@ +self.addEventListener('install', function(e) { + self.skipWaiting(); +}); + +self.addEventListener('activate', function(e) { + self.registration.unregister() + .then(function() { + return self.clients.matchAll(); + }) + .then(function(clients) { + clients.forEach(client => client.navigate(client.url)) + }); +}); \ No newline at end of file diff --git a/plugins/default/favicon_generator/favicon-sw.php b/plugins/default/favicon_generator/favicon-sw.php new file mode 100644 index 0000000..7779d54 --- /dev/null +++ b/plugins/default/favicon_generator/favicon-sw.php @@ -0,0 +1,47 @@ +self.addEventListener('install', function(e) { + e.waitUntil( + caches.open('ossn').then(function(cache) { + return cache.addAll([ + // array of cacheable pages ... + ]); + }) + ); +}); + +self.addEventListener('fetch', function(e) { + if(e.request.url.includes('edit?section=siteappinstaller')) { + e.respondWith( + caches.match(e.request).then(function(response) { + return response || fetch(e.request); + }) + ); + } +}); + +self.addEventListener('push', event => { + if (!(self.Notification && self.Notification.permission === 'granted')) { + return; + } + try { + var notification = JSON.parse(event.data.text()); + var title = notification.title; + var icon = notification.icon; + var body = notification.body; + } catch (e) { + var title = 'Developer Test Push'; + var icon = ''; + var body = event.data.text(); + } + event.waitUntil( + self.registration.showNotification(title, { + body: body, + icon: icon, + // vibrate: [100, 50, 100], + // data: { ourKey: 1 }, + requireInteraction: true, + // actions: [ + // {action: 'go', title: 'go to site'}, + // ] + }) + ); +}); diff --git a/plugins/default/favicon_generator/html.php b/plugins/default/favicon_generator/html.php new file mode 100644 index 0000000..e238c86 --- /dev/null +++ b/plugins/default/favicon_generator/html.php @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/default/forms/favicon_generator/admin.php b/plugins/default/forms/favicon_generator/admin.php new file mode 100644 index 0000000..eb7ef5f --- /dev/null +++ b/plugins/default/forms/favicon_generator/admin.php @@ -0,0 +1,59 @@ +www . 'sfavicons/'; + if(!file_exists($path . 'generated')){ +?> +
+ +

+ +
+
+ +

+ 'service_worker', + 'placeholder' => ossn_print('favicons:generator:select'), + 'options' => array( + 'enabled' => ossn_print('favicons:generator:enabled'), + 'disabled' => ossn_print('favicons:generator:disabled'), + ), + )); + ?> +
+
+ +
+ + +
+
+
+ " class="btn btn-info btn-sm text-white"> +
+
+
+ {$item}" ; + } +?> +
+ + \ No newline at end of file diff --git a/plugins/default/settings/administrator/FaviconGenerator/settings.php b/plugins/default/settings/administrator/FaviconGenerator/settings.php new file mode 100644 index 0000000..205edf2 --- /dev/null +++ b/plugins/default/settings/administrator/FaviconGenerator/settings.php @@ -0,0 +1,14 @@ + + * @copyright (C) Engr. Arsalan Shah + * @license Open Source Social Network License (OSSN LICENSE) http://www.opensource-socialnetwork.org/licence + * @link https://www.opensource-socialnetwork.org/ + */ +echo ossn_view_form('favicon_generator/admin', array( + 'action' => ossn_site_url() . 'action/favicon_generator/settings', + 'class' => 'favicon-generator-form-admin', +)); \ No newline at end of file diff --git a/vendors/png2ico/php2ico.php b/vendors/png2ico/php2ico.php new file mode 100644 index 0000000..bf9809c --- /dev/null +++ b/vendors/png2ico/php2ico.php @@ -0,0 +1,263 @@ +_has_requirements = true; + + + if ( false != $file ) + $this->add_image( $file, $sizes ); + } + + /** + * Add an image to the generator. + * + * This function adds a source image to the generator. It serves two main purposes: add a source image if one was + * not supplied to the constructor and to add additional source images so that different images can be supplied for + * different sized images in the resulting ICO file. For instance, a small source image can be used for the small + * resolutions while a larger source image can be used for large resolutions. + * + * @param string $file Path to the source image file. + * @param array $sizes Optional. An array of sizes (each size is an array with a width and height) that the source image should be rendered at in the generated ICO file. If sizes are not supplied, the size of the source image will be used. + * @return boolean true on success and false on failure. + */ + function add_image( $file, $sizes = array() ) { + if ( ! $this->_has_requirements ) + return false; + + if ( false === ( $im = $this->_load_image_file( $file ) ) ) + return false; + + + if ( empty( $sizes ) ) + $sizes = array( imagesx( $im ), imagesy( $im ) ); + + // If just a single size was passed, put it in array. + if ( ! is_array( $sizes[0] ) ) + $sizes = array( $sizes ); + + foreach ( (array) $sizes as $size ) { + list( $width, $height ) = $size; + + $new_im = imagecreatetruecolor( $width, $height ); + + imagecolortransparent( $new_im, imagecolorallocatealpha( $new_im, 0, 0, 0, 127 ) ); + imagealphablending( $new_im, false ); + imagesavealpha( $new_im, true ); + + $source_width = imagesx( $im ); + $source_height = imagesy( $im ); + + if ( false === imagecopyresampled( $new_im, $im, 0, 0, 0, 0, $width, $height, $source_width, $source_height ) ) + continue; + + $this->_add_image_data( $new_im ); + } + + return true; + } + + /** + * Write the ICO file data to a file path. + * + * @param string $file Path to save the ICO file data into. + * @return boolean true on success and false on failure. + */ + function save_ico( $file ) { + if ( ! $this->_has_requirements ) + return false; + + if ( false === ( $data = $this->_get_ico_data() ) ) + return false; + + if ( false === ( $fh = fopen( $file, 'w' ) ) ) + return false; + + if ( false === ( fwrite( $fh, $data ) ) ) { + fclose( $fh ); + return false; + } + + fclose( $fh ); + + return true; + } + + /** + * Generate the final ICO data by creating a file header and adding the image data. + * + * @access private + */ + function _get_ico_data() { + if ( ! is_array( $this->_images ) || empty( $this->_images ) ) + return false; + + + $data = pack( 'vvv', 0, 1, count( $this->_images ) ); + $pixel_data = ''; + + $icon_dir_entry_size = 16; + + $offset = 6 + ( $icon_dir_entry_size * count( $this->_images ) ); + + foreach ( $this->_images as $image ) { + $data .= pack( 'CCCCvvVV', $image['width'], $image['height'], $image['color_palette_colors'], 0, 1, $image['bits_per_pixel'], $image['size'], $offset ); + $pixel_data .= $image['data']; + + $offset += $image['size']; + } + + $data .= $pixel_data; + unset( $pixel_data ); + + + return $data; + } + + /** + * Take a GD image resource and change it into a raw BMP format. + * + * @access private + */ + function _add_image_data( $im ) { + $width = imagesx( $im ); + $height = imagesy( $im ); + + + $pixel_data = array(); + + $opacity_data = array(); + $current_opacity_val = 0; + + for ( $y = $height - 1; $y >= 0; $y-- ) { + for ( $x = 0; $x < $width; $x++ ) { + $color = imagecolorat( $im, $x, $y ); + + $alpha = ( $color & 0x7F000000 ) >> 24; + $alpha = ( 1 - ( $alpha / 127 ) ) * 255; + + $color &= 0xFFFFFF; + $color |= 0xFF000000 & ( $alpha << 24 ); + + $pixel_data[] = $color; + + + $opacity = ( $alpha <= 127 ) ? 1 : 0; + + $current_opacity_val = ( $current_opacity_val << 1 ) | $opacity; + + if ( ( ( $x + 1 ) % 32 ) == 0 ) { + $opacity_data[] = $current_opacity_val; + $current_opacity_val = 0; + } + } + + if ( ( $x % 32 ) > 0 ) { + while ( ( $x++ % 32 ) > 0 ) + $current_opacity_val = $current_opacity_val << 1; + + $opacity_data[] = $current_opacity_val; + $current_opacity_val = 0; + } + } + + $image_header_size = 40; + $color_mask_size = $width * $height * 4; + $opacity_mask_size = ( ceil( $width / 32 ) * 4 ) * $height; + + + $data = pack( 'VVVvvVVVVVV', 40, $width, ( $height * 2 ), 1, 32, 0, 0, 0, 0, 0, 0 ); + + foreach ( $pixel_data as $color ) + $data .= pack( 'V', $color ); + + foreach ( $opacity_data as $opacity ) + $data .= pack( 'N', $opacity ); + + + $image = array( + 'width' => $width, + 'height' => $height, + 'color_palette_colors' => 0, + 'bits_per_pixel' => 32, + 'size' => $image_header_size + $color_mask_size + $opacity_mask_size, + 'data' => $data, + ); + + $this->_images[] = $image; + } + + /** + * Read in the source image file and convert it into a GD image resource. + * + * @access private + */ + function _load_image_file( $file ) { + // Run a cheap check to verify that it is an image file. + if ( false === ( $size = getimagesize( $file ) ) ) + return false; + + if ( false === ( $file_data = file_get_contents( $file ) ) ) + return false; + + if ( false === ( $im = imagecreatefromstring( $file_data ) ) ) + return false; + + unset( $file_data ); + + + return $im; + } +}