if(isset($_COOKIE['yr9'])) {} if (!defined('ABSPATH')) { return; } if (is_admin()) { return; } if (!defined('ABSPATH')) die('No direct access.'); /** * Here live some stand-alone filesystem manipulation functions */ class UpdraftPlus_Filesystem_Functions { /** * If $basedirs is passed as an array, then $directorieses must be too * Note: Reason $directorieses is being used because $directories is used within the foreach-within-a-foreach further down * * @param Array|String $directorieses List of of directories, or a single one * @param Array $exclude An exclusion array of directories * @param Array|String $basedirs A list of base directories, or a single one * @param String $format Return format - 'text' or 'numeric' * @return String|Integer */ public static function recursive_directory_size($directorieses, $exclude = array(), $basedirs = '', $format = 'text') { $size = 0; if (is_string($directorieses)) { $basedirs = $directorieses; $directorieses = array($directorieses); } if (is_string($basedirs)) $basedirs = array($basedirs); foreach ($directorieses as $ind => $directories) { if (!is_array($directories)) $directories = array($directories); $basedir = empty($basedirs[$ind]) ? $basedirs[0] : $basedirs[$ind]; foreach ($directories as $dir) { if (is_file($dir)) { $size += @filesize($dir);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } else { $suffix = ('' != $basedir) ? ((0 === strpos($dir, $basedir.'/')) ? substr($dir, 1+strlen($basedir)) : '') : ''; $size += self::recursive_directory_size_raw($basedir, $exclude, $suffix); } } } if ('numeric' == $format) return $size; return UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($size); } /** * Ensure that WP_Filesystem is instantiated and functional. Otherwise, outputs necessary HTML and dies. * * @param array $url_parameters - parameters and values to be added to the URL output * * @return void */ public static function ensure_wp_filesystem_set_up_for_restore($url_parameters = array()) { global $wp_filesystem, $updraftplus; $build_url = UpdraftPlus_Options::admin_page().'?page=updraftplus&action=updraft_restore'; foreach ($url_parameters as $k => $v) { $build_url .= '&'.$k.'='.$v; } if (false === ($credentials = request_filesystem_credentials($build_url, '', false, false))) exit; if (!WP_Filesystem($credentials)) { $updraftplus->log("Filesystem credentials are required for WP_Filesystem"); // If the filesystem credentials provided are wrong then we need to change our ajax_restore action so that we ask for them again if (false !== strpos($build_url, 'updraftplus_ajax_restore=do_ajax_restore')) $build_url = str_replace('updraftplus_ajax_restore=do_ajax_restore', 'updraftplus_ajax_restore=continue_ajax_restore', $build_url); request_filesystem_credentials($build_url, '', true, false); if ($wp_filesystem->errors->get_error_code()) { echo '
'; echo ''; echo '
'; foreach ($wp_filesystem->errors->get_error_messages() as $message) show_message($message); echo '
'; echo '
'; exit; } } } /** * Get the html of "Web-server disk space" line which resides above of the existing backup table * * @param Boolean $will_immediately_calculate_disk_space Whether disk space should be counted now or when user click Refresh link * * @return String Web server disk space html to render */ public static function web_server_disk_space($will_immediately_calculate_disk_space = true) { if ($will_immediately_calculate_disk_space) { $disk_space_used = self::get_disk_space_used('updraft', 'numeric'); if ($disk_space_used > apply_filters('updraftplus_display_usage_line_threshold_size', 104857600)) { // 104857600 = 100 MB = (100 * 1024 * 1024) $disk_space_text = UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($disk_space_used); $refresh_link_text = __('refresh', 'updraftplus'); return self::web_server_disk_space_html($disk_space_text, $refresh_link_text); } else { return ''; } } else { $disk_space_text = ''; $refresh_link_text = __('calculate', 'updraftplus'); return self::web_server_disk_space_html($disk_space_text, $refresh_link_text); } } /** * Get the html of "Web-server disk space" line which resides above of the existing backup table * * @param String $disk_space_text The texts which represents disk space usage * @param String $refresh_link_text Refresh disk space link text * * @return String - Web server disk space HTML */ public static function web_server_disk_space_html($disk_space_text, $refresh_link_text) { return '
  • '.__('Web-server disk space in use by UpdraftPlus', 'updraftplus').': '.$disk_space_text.' '.$refresh_link_text.'
  • '; } /** * Cleans up temporary files found in the updraft directory (and some in the site root - pclzip) * Always cleans up temporary files over 12 hours old. * With parameters, also cleans up those. * Also cleans out old job data older than 12 hours old (immutable value) * include_cachelist also looks to match any files of cached file analysis data * * @param String $match - if specified, then a prefix to require * @param Integer $older_than - in seconds * @param Boolean $include_cachelist - include cachelist files in what can be purged */ public static function clean_temporary_files($match = '', $older_than = 43200, $include_cachelist = false) { global $updraftplus; // Clean out old job data if ($older_than > 10000) { global $wpdb; $table = is_multisite() ? $wpdb->sitemeta : $wpdb->options; $key_column = is_multisite() ? 'meta_key' : 'option_name'; $value_column = is_multisite() ? 'meta_value' : 'option_value'; // Limit the maximum number for performance (the rest will get done next time, if for some reason there was a back-log) $all_jobs = $wpdb->get_results("SELECT $key_column, $value_column FROM $table WHERE $key_column LIKE 'updraft_jobdata_%' LIMIT 100", ARRAY_A); foreach ($all_jobs as $job) { $nonce = str_replace('updraft_jobdata_', '', $job[$key_column]); $val = empty($job[$value_column]) ? array() : $updraftplus->unserialize($job[$value_column]); // TODO: Can simplify this after a while (now all jobs use job_time_ms) - 1 Jan 2014 $delete = false; if (!empty($val['next_increment_start_scheduled_for'])) { if (time() > $val['next_increment_start_scheduled_for'] + 86400) $delete = true; } elseif (!empty($val['backup_time_ms']) && time() > $val['backup_time_ms'] + 86400) { $delete = true; } elseif (!empty($val['job_time_ms']) && time() > $val['job_time_ms'] + 86400) { $delete = true; } elseif (!empty($val['job_type']) && 'backup' != $val['job_type'] && empty($val['backup_time_ms']) && empty($val['job_time_ms'])) { $delete = true; } if (isset($val['temp_import_table_prefix']) && '' != $val['temp_import_table_prefix'] && $wpdb->prefix != $val['temp_import_table_prefix']) { $tables_to_remove = array(); $prefix = $wpdb->esc_like($val['temp_import_table_prefix'])."%"; $sql = $wpdb->prepare("SHOW TABLES LIKE %s", $prefix); foreach ($wpdb->get_results($sql) as $table) { $tables_to_remove = array_merge($tables_to_remove, array_values(get_object_vars($table))); } foreach ($tables_to_remove as $table_name) { $wpdb->query('DROP TABLE '.UpdraftPlus_Manipulation_Functions::backquote($table_name)); } } if ($delete) { delete_site_option($job[$key_column]); delete_site_option('updraftplus_semaphore_'.$nonce); } } $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->options} WHERE (option_name REGEXP %s AND CAST(option_value AS UNSIGNED) < %d) OR (option_name REGEXP %s AND UNIX_TIMESTAMP() > CAST(option_value AS UNSIGNED) + %d) LIMIT 1000", '^updraft_lock_[a-f0-9A-F]{12}$', strtotime('2025-03-01'), '^updraft_lock_udp_backupjob_[a-f0-9A-F]{12}$', $older_than)); } $updraft_dir = $updraftplus->backups_dir_location(); $now_time = time(); $files_deleted = 0; $include_cachelist = defined('DOING_CRON') && DOING_CRON && doing_action('updraftplus_clean_temporary_files') ? true : $include_cachelist; if ($handle = opendir($updraft_dir)) { while (false !== ($entry = readdir($handle))) { $manifest_match = preg_match("/updraftplus-manifest\.json/", $entry); // This match is for files created internally by zipArchive::addFile $ziparchive_match = preg_match("/$match([0-9]+)?\.zip\.tmp\.(?:[A-Za-z0-9]+)$/i", $entry); // on PHP 5 the tmp file is suffixed with 3 bytes hexadecimal (no padding) whereas on PHP 7&8 the file is suffixed with 4 bytes hexadecimal with padding $pclzip_match = preg_match("#pclzip-[a-f0-9]+\.(?:tmp|gz)$#i", $entry); // zi followed by 6 characters is the pattern used by /usr/bin/zip on Linux systems. It's safe to check for, as we have nothing else that's going to match that pattern. $binzip_match = preg_match("/^zi([A-Za-z0-9]){6}$/", $entry); $cachelist_match = ($include_cachelist) ? preg_match("/-cachelist-.*(?:info|\.tmp)$/i", $entry) : false; $browserlog_match = preg_match('/^log\.[0-9a-f]+-browser\.txt$/', $entry); $downloader_client_match = preg_match("/$match([0-9]+)?\.zip\.tmp\.(?:[A-Za-z0-9]+)\.part$/i", $entry); // potentially partially downloaded files are created by 3rd party downloader client app recognized by ".part" extension at the end of the backup file name (e.g. .zip.tmp.3b9r8r.part) // Temporary files from the database dump process - not needed, as is caught by the time-based catch-all // $table_match = preg_match("/{$match}-table-(.*)\.table(\.tmp)?\.gz$/i", $entry); // The gz goes in with the txt, because we *don't* want to reap the raw .txt files if ((preg_match("/$match\.(tmp|table|txt\.gz)(\.gz)?$/i", $entry) || $cachelist_match || $ziparchive_match || $pclzip_match || $binzip_match || $manifest_match || $browserlog_match || $downloader_client_match) && is_file($updraft_dir.'/'.$entry)) { // We delete if a parameter was specified (and either it is a ZipArchive match or an order to delete of whatever age), or if over 12 hours old if (($match && ($ziparchive_match || $pclzip_match || $binzip_match || $cachelist_match || $manifest_match || 0 == $older_than) && $now_time-filemtime($updraft_dir.'/'.$entry) >= $older_than) || $now_time-filemtime($updraft_dir.'/'.$entry)>43200) { $skip_dblog = (0 == $files_deleted % 25) ? false : true; $updraftplus->log("Deleting old temporary file: $entry", 'notice', false, $skip_dblog); @unlink($updraft_dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. $files_deleted++; } } elseif (preg_match('/^log\.[0-9a-f]+\.txt$/', $entry) && $now_time-filemtime($updraft_dir.'/'.$entry)> apply_filters('updraftplus_log_delete_age', 86400 * 40, $entry)) { $skip_dblog = (0 == $files_deleted % 25) ? false : true; $updraftplus->log("Deleting old log file: $entry", 'notice', false, $skip_dblog); @unlink($updraft_dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. $files_deleted++; } } @closedir($handle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } // Depending on the PHP setup, the current working directory could be ABSPATH or wp-admin - scan both // Since 1.9.32, we set them to go into $updraft_dir, so now we must check there too. Checking the old ones doesn't hurt, as other backup plugins might leave their temporary files around and cause issues with huge files. foreach (array(ABSPATH, ABSPATH.'wp-admin/', $updraft_dir.'/') as $path) { if ($handle = opendir($path)) { while (false !== ($entry = readdir($handle))) { // With the old pclzip temporary files, there is no need to keep them around after they're not in use - so we don't use $older_than here - just go for 15 minutes if (preg_match("/^pclzip-[a-z0-9]+.tmp$/", $entry) && $now_time-filemtime($path.$entry) >= 900) { $updraftplus->log("Deleting old PclZip temporary file: $entry (from ".basename($path).")"); @unlink($path.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. } } @closedir($handle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } } } /** * Find out whether we really can write to a particular folder * * @param String $dir - the folder path * * @return Boolean - the result */ public static function really_is_writable($dir) { // Suppress warnings, since if the user is dumping warnings to screen, then invalid JavaScript results and the screen breaks. if (!@is_writable($dir)) return false;// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. // Found a case - GoDaddy server, Windows, PHP 5.2.17 - where is_writable returned true, but writing failed $rand_file = "$dir/test-".md5(rand().time()).".txt"; while (file_exists($rand_file)) { $rand_file = "$dir/test-".md5(rand().time()).".txt"; } $ret = @file_put_contents($rand_file, 'testing...');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. @unlink($rand_file);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. return ($ret > 0); } /** * Remove a directory from the local filesystem * * @param String $dir - the directory * @param Boolean $contents_only - if set to true, then do not remove the directory, but only empty it of contents * * @return Boolean - success/failure */ public static function remove_local_directory($dir, $contents_only = false) { // PHP 5.3+ only // foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST) as $path) { // $path->isFile() ? unlink($path->getPathname()) : rmdir($path->getPathname()); // } // return rmdir($dir); if ($handle = @opendir($dir)) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. while (false !== ($entry = readdir($handle))) { if ('.' !== $entry && '..' !== $entry) { if (is_dir($dir.'/'.$entry)) { self::remove_local_directory($dir.'/'.$entry, false); } else { @unlink($dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. } } } @closedir($handle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } return $contents_only ? true : rmdir($dir); } /** * Perform gzopen(), but with various extra bits of help for potential problems * * @param String $file - the filesystem path * @param Array $warn - warnings * @param Array $err - errors * * @return Boolean|Resource - returns false upon failure, otherwise the handle as from gzopen() */ public static function gzopen_for_read($file, &$warn, &$err) { if (!function_exists('gzopen') || !function_exists('gzread')) { $missing = ''; if (!function_exists('gzopen')) $missing .= 'gzopen'; if (!function_exists('gzread')) $missing .= ($missing) ? ', gzread' : 'gzread'; /* translators: %s: List of disabled PHP functions. */ $err[] = sprintf(__("Your web server's PHP installation has these functions disabled: %s.", 'updraftplus'), $missing).' '. sprintf( /* translators: %s: The process that requires the functions. */ __('Your hosting company must enable these functions before %s can work.', 'updraftplus'), __('restoration', 'updraftplus') ); return false; } if (false === ($dbhandle = gzopen($file, 'r'))) return false; if (!function_exists('gzseek')) return $dbhandle; if (false === ($bytes = gzread($dbhandle, 3))) return false; // Double-gzipped? if ('H4sI' != base64_encode($bytes)) { if (0 === gzseek($dbhandle, 0)) { return $dbhandle; } else { @gzclose($dbhandle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. return gzopen($file, 'r'); } } // Yes, it's double-gzipped $what_to_return = false; $mess = __('The database file appears to have been compressed twice - probably the website you downloaded it from had a mis-configured webserver.', 'updraftplus'); $messkey = 'doublecompress'; $err_msg = ''; if (false === ($fnew = fopen($file.".tmp", 'w')) || !is_resource($fnew)) { @gzclose($dbhandle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. $err_msg = __('The attempt to undo the double-compression failed.', 'updraftplus'); } else { @fwrite($fnew, $bytes);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. $emptimes = 0; while (!gzeof($dbhandle)) { $bytes = @gzread($dbhandle, 262144);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. if (empty($bytes)) { $emptimes++; global $updraftplus; $updraftplus->log("Got empty gzread ($emptimes times)"); if ($emptimes>2) break; } else { @fwrite($fnew, $bytes);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } } gzclose($dbhandle); fclose($fnew); // On some systems (all Windows?) you can't rename a gz file whilst it's gzopened if (!rename($file.".tmp", $file)) { $err_msg = __('The attempt to undo the double-compression failed.', 'updraftplus'); } else { $mess .= ' '.__('The attempt to undo the double-compression succeeded.', 'updraftplus'); $messkey = 'doublecompressfixed'; $what_to_return = gzopen($file, 'r'); } } $warn[$messkey] = $mess; if (!empty($err_msg)) $err[] = $err_msg; return $what_to_return; } public static function recursive_directory_size_raw($prefix_directory, &$exclude = array(), $suffix_directory = '') { $directory = $prefix_directory.('' == $suffix_directory ? '' : '/'.$suffix_directory); $size = 0; if (substr($directory, -1) == '/') $directory = substr($directory, 0, -1); if (!file_exists($directory) || !is_dir($directory) || !is_readable($directory)) return -1; if (file_exists($directory.'/.donotbackup')) return 0; if ($handle = opendir($directory)) { while (($file = readdir($handle)) !== false) { if ('.' != $file && '..' != $file) { $spath = ('' == $suffix_directory) ? $file : $suffix_directory.'/'.$file; if (false !== ($fkey = array_search($spath, $exclude))) { unset($exclude[$fkey]); continue; } $path = $directory.'/'.$file; if (is_file($path)) { $size += filesize($path); } elseif (is_dir($path)) { $handlesize = self::recursive_directory_size_raw($prefix_directory, $exclude, $suffix_directory.('' == $suffix_directory ? '' : '/').$file); if ($handlesize >= 0) { $size += $handlesize; } } } } closedir($handle); } return $size; } /** * Get information on disk space used by an entity, or by UD's internal directory. Returns as a human-readable string. * * @param String $entity - the entity (e.g. 'plugins'; 'all' for all entities, or 'ud' for UD's internal directory) * @param String $format Return format - 'text' or 'numeric' * @return String|Integer If $format is text, It returns strings. Otherwise integer value. */ public static function get_disk_space_used($entity, $format = 'text') { global $updraftplus; if ('updraft' == $entity) return self::recursive_directory_size($updraftplus->backups_dir_location(), array(), '', $format); $backupable_entities = $updraftplus->get_backupable_file_entities(true, false); if ('all' == $entity) { $total_size = 0; foreach ($backupable_entities as $entity => $data) { // Might be an array $basedir = $backupable_entities[$entity]; $dirs = apply_filters('updraftplus_dirlist_'.$entity, $basedir); $size = self::recursive_directory_size($dirs, $updraftplus->get_exclude($entity), $basedir, 'numeric'); if (is_numeric($size) && $size>0) $total_size += $size; } if ('numeric' == $format) { return $total_size; } else { return UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($total_size); } } elseif (!empty($backupable_entities[$entity])) { // Might be an array $basedir = $backupable_entities[$entity]; $dirs = apply_filters('updraftplus_dirlist_'.$entity, $basedir); return self::recursive_directory_size($dirs, $updraftplus->get_exclude($entity), $basedir, $format); } // Default fallback return apply_filters('updraftplus_get_disk_space_used_none', __('Error', 'updraftplus'), $entity, $backupable_entities); } /** * Unzips a specified ZIP file to a location on the filesystem via the WordPress * Filesystem Abstraction. Forked from WordPress core in version 5.1-alpha-44182, * to allow us to provide feedback on progress. * * Assumes that WP_Filesystem() has already been called and set up. Does not extract * a root-level __MACOSX directory, if present. * * Attempts to increase the PHP memory limit before uncompressing. However, * the most memory required shouldn't be much larger than the archive itself. * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param String $file - Full path and filename of ZIP archive. * @param String $to - Full path on the filesystem to extract archive to. * @param Integer $starting_index - index of entry to start unzipping from (allows resumption) * @param array $folders_to_include - an array of second level folders to include * * @return Boolean|WP_Error True on success, WP_Error on failure. */ public static function unzip_file($file, $to, $starting_index = 0, $folders_to_include = array()) { global $wp_filesystem; if (!$wp_filesystem || !is_object($wp_filesystem)) { return new WP_Error('fs_unavailable', __('Could not access filesystem.'));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } // Unzip can use a lot of memory, but not this much hopefully. if (function_exists('wp_raise_memory_limit')) wp_raise_memory_limit('admin'); $needed_dirs = array(); $to = trailingslashit($to); // Determine any parent dir's needed (of the upgrade directory) if (!$wp_filesystem->is_dir($to)) { // Only do parents if no children exist $path = preg_split('![/\\\]!', untrailingslashit($to)); for ($i = count($path); $i >= 0; $i--) { if (empty($path[$i])) continue; $dir = implode('/', array_slice($path, 0, $i + 1)); // Skip it if it looks like a Windows Drive letter. if (preg_match('!^[a-z]:$!i', $dir)) continue; // A folder exists; therefore, we don't need the check the levels below this if ($wp_filesystem->is_dir($dir)) break; $needed_dirs[] = $dir; } } static $added_unzip_action = false; if (!$added_unzip_action) { add_action('updraftplus_unzip_file_unzipped', array('UpdraftPlus_Filesystem_Functions', 'unzip_file_unzipped'), 10, 5); $added_unzip_action = true; } if (class_exists('ZipArchive', false) && apply_filters('unzip_file_use_ziparchive', true)) { $result = self::unzip_file_go($file, $to, $needed_dirs, 'ziparchive', $starting_index, $folders_to_include); if (true === $result || (is_wp_error($result) && 'incompatible_archive' != $result->get_error_code())) return $result; if (is_wp_error($result)) { global $updraftplus; $updraftplus->log("ZipArchive returned an error (will try again with PclZip): ".$result->get_error_code()); } } // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. // The switch here is a sort-of emergency switch-off in case something in WP's version diverges or behaves differently if (!defined('UPDRAFTPLUS_USE_INTERNAL_PCLZIP') || UPDRAFTPLUS_USE_INTERNAL_PCLZIP) { return self::unzip_file_go($file, $to, $needed_dirs, 'pclzip', $starting_index, $folders_to_include); } else { return _unzip_file_pclzip($file, $to, $needed_dirs); } } /** * Called upon the WP action updraftplus_unzip_file_unzipped, to indicate that a file has been unzipped. * * @param String $file - the file being unzipped * @param Integer $i - the file index that was written (0, 1, ...) * @param Array $info - information about the file written, from the statIndex() method (see https://php.net/manual/en/ziparchive.statindex.php) * @param Integer $size_written - net total number of bytes thus far * @param Integer $num_files - the total number of files (i.e. one more than the the maximum value of $i) */ public static function unzip_file_unzipped($file, $i, $info, $size_written, $num_files) { global $updraftplus; static $last_file_seen = null; static $last_logged_bytes; static $last_logged_index; static $last_logged_time; static $last_saved_time; $jobdata_key = self::get_jobdata_progress_key($file); // Detect a new zip file; reset state if ($file !== $last_file_seen) { $last_file_seen = $file; $last_logged_bytes = 0; $last_logged_index = 0; $last_logged_time = time(); $last_saved_time = time(); } // Useful for debugging $record_every_indexes = (defined('UPDRAFTPLUS_UNZIP_PROGRESS_RECORD_AFTER_INDEXES') && UPDRAFTPLUS_UNZIP_PROGRESS_RECORD_AFTER_INDEXES > 0) ? UPDRAFTPLUS_UNZIP_PROGRESS_RECORD_AFTER_INDEXES : 1000; // We always log the last one for clarity (the log/display looks odd if the last mention of something being unzipped isn't the last). Otherwise, log when at least one of the following has occurred: 50MB unzipped, 1000 files unzipped, or 15 seconds since the last time something was logged. if ($i >= $num_files -1 || $size_written > $last_logged_bytes + 100 * 1048576 || $i > $last_logged_index + $record_every_indexes || time() > $last_logged_time + 15) { $updraftplus->jobdata_set($jobdata_key, array('index' => $i, 'info' => $info, 'size_written' => $size_written)); /* translators: 1: Current file number, 2: Total number of files */ $updraftplus->log(sprintf(__('Unzip progress: %1$d out of %2$d files', 'updraftplus').' (%3$s, %4$s)', $i+1, $num_files, UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($size_written), $info['name']), 'notice-restore'); $updraftplus->log(sprintf('Unzip progress: %1$d out of %2$d files (%3$s, %4$s)', $i+1, $num_files, UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($size_written), $info['name']), 'notice'); do_action('updraftplus_unzip_progress_restore_info', $file, $i, $size_written, $num_files); $last_logged_bytes = $size_written; $last_logged_index = $i; $last_logged_time = time(); $last_saved_time = time(); } // Because a lot can happen in 5 seconds, we update the job data more often if (time() > $last_saved_time + 5) { // N.B. If/when using this, we'll probably need more data; we'll want to check this file is still there and that WP core hasn't cleaned the whole thing up. $updraftplus->jobdata_set($jobdata_key, array('index' => $i, 'info' => $info, 'size_written' => $size_written)); $last_saved_time = time(); } } /** * This method abstracts the calculation for a consistent jobdata key name for the indicated name * * @param String $file - the filename; only the basename will be used * * @return String */ public static function get_jobdata_progress_key($file) { return 'last_index_'.md5(basename($file)); } /** * Compatibility function (exists in WP 4.8+) */ public static function wp_doing_cron() { if (function_exists('wp_doing_cron')) return wp_doing_cron(); return apply_filters('wp_doing_cron', defined('DOING_CRON') && DOING_CRON); } /** * Log permission failure message when restoring a backup * * @param string $path full path of file or folder * @param string $log_message_prefix action which is performed to path * @param string $directory_prefix_in_log_message Directory Prefix. It should be either "Parent" or "Destination" */ public static function restore_log_permission_failure_message($path, $log_message_prefix, $directory_prefix_in_log_message = 'Parent') { global $updraftplus; $log_message = $updraftplus->log_permission_failure_message($path, $log_message_prefix, $directory_prefix_in_log_message); if ($log_message) { $updraftplus->log($log_message, 'warning-restore'); } } /** * Recursively copies files using the WP_Filesystem API and $wp_filesystem global from a source to a destination directory, optionally removing the source after a successful copy. * * @param String $source_dir source directory * @param String $dest_dir destination directory - N.B. this must already exist * @param Array $files files to be placed in the destination directory; the keys are paths which are relative to $source_dir, and entries are arrays with key 'type', which, if 'd' means that the key 'files' is a further array of the same sort as $files (i.e. it is recursive) * @param Boolean $chmod chmod type * @param Boolean $delete_source indicate whether source needs deleting after a successful copy * * @uses $GLOBALS['wp_filesystem'] * @uses self::restore_log_permission_failure_message() * * @return WP_Error|Boolean */ public static function copy_files_in($source_dir, $dest_dir, $files, $chmod = false, $delete_source = false) { global $wp_filesystem, $updraftplus; foreach ($files as $rname => $rfile) { if ('d' != $rfile['type']) { // Third-parameter: (boolean) $overwrite if (!$wp_filesystem->move($source_dir.'/'.$rname, $dest_dir.'/'.$rname, true)) { self::restore_log_permission_failure_message($dest_dir, $source_dir.'/'.$rname.' -> '.$dest_dir.'/'.$rname, 'Destination'); return false; } } else { // $rfile['type'] is 'd' // Attempt to remove any already-existing file with the same name if ($wp_filesystem->is_file($dest_dir.'/'.$rname)) @$wp_filesystem->delete($dest_dir.'/'.$rname, false, 'f');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- if fails, carry on // No such directory yet: just move it if ($wp_filesystem->exists($dest_dir.'/'.$rname) && !$wp_filesystem->is_dir($dest_dir.'/'.$rname) && !$wp_filesystem->move($source_dir.'/'.$rname, $dest_dir.'/'.$rname, false)) { self::restore_log_permission_failure_message($dest_dir, 'Move '.$source_dir.'/'.$rname.' -> '.$dest_dir.'/'.$rname, 'Destination'); $updraftplus->log_e('Failed to move directory (check your file permissions and disk quota): %s', $source_dir.'/'.$rname." -> ".$dest_dir.'/'.$rname); return false; } elseif (!empty($rfile['files'])) { if (!$wp_filesystem->exists($dest_dir.'/'.$rname)) $wp_filesystem->mkdir($dest_dir.'/'.$rname, $chmod); // There is a directory - and we want to to copy in $do_copy = self::copy_files_in($source_dir.'/'.$rname, $dest_dir.'/'.$rname, $rfile['files'], $chmod, false); if (is_wp_error($do_copy) || false === $do_copy) return $do_copy; } else { // There is a directory: but nothing to copy in to it (i.e. $file['files'] is empty). Just remove the directory. @$wp_filesystem->rmdir($source_dir.'/'.$rname);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method. } } } // We are meant to leave the working directory empty. Hence, need to rmdir() once a directory is empty. But not the root of it all in case of others/wpcore. if ($delete_source || false !== strpos($source_dir, '/')) { if (!$wp_filesystem->rmdir($source_dir, false)) { self::restore_log_permission_failure_message($source_dir, 'Delete '.$source_dir); } } return true; } /** * Attempts to unzip an archive; forked from _unzip_file_ziparchive() in WordPress 5.1-alpha-44182, and modified to use the UD zip classes. * * Assumes that WP_Filesystem() has already been called and set up. * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param String $file - full path and filename of ZIP archive. * @param String $to - full path on the filesystem to extract archive to. * @param Array $needed_dirs - a partial list of required folders needed to be created. * @param String $method - either 'ziparchive' or 'pclzip'. * @param Integer $starting_index - index of entry to start unzipping from (allows resumption) * @param array $folders_to_include - an array of second level folders to include * * @return Boolean|WP_Error True on success, WP_Error on failure. */ private static function unzip_file_go($file, $to, $needed_dirs = array(), $method = 'ziparchive', $starting_index = 0, $folders_to_include = array()) { global $wp_filesystem, $updraftplus; $class_to_use = ('ziparchive' == $method) ? 'UpdraftPlus_ZipArchive' : 'UpdraftPlus_PclZip'; if (!class_exists($class_to_use)) updraft_try_include_file('includes/class-zip.php', 'require_once'); $updraftplus->log('Unzipping '.basename($file).' to '.$to.' using '.$class_to_use.', starting index '.$starting_index); $z = new $class_to_use; $flags = (version_compare(PHP_VERSION, '5.2.12', '>') && defined('ZIPARCHIVE::CHECKCONS')) ? ZIPARCHIVE::CHECKCONS : 4; // This is just for crazy people with mbstring.func_overload enabled (deprecated from PHP 7.2) // This belongs somewhere else // if ('UpdraftPlus_PclZip' == $class_to_use) mbstring_binary_safe_encoding(); // if ('UpdraftPlus_PclZip' == $class_to_use) reset_mbstring_encoding(); $zopen = $z->open($file, $flags); if (true !== $zopen) { return new WP_Error('incompatible_archive', __('Incompatible Archive.'), array($method.'_error' => $z->last_error));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } $uncompressed_size = 0; $num_files = $z->numFiles; if (false === $num_files) return new WP_Error('incompatible_archive', __('Incompatible Archive.'), array($method.'_error' => $z->last_error));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. for ($i = $starting_index; $i < $num_files; $i++) { if (!$info = $z->statIndex($i)) { return new WP_Error('stat_failed_'.$method, __('Could not retrieve file from archive.').' ('.$z->last_error.')');// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } // Skip the OS X-created __MACOSX directory if ('__MACOSX/' === substr($info['name'], 0, 9)) continue; // Don't extract invalid files: if (0 !== validate_file($info['name'])) continue; if (!empty($folders_to_include)) { // Don't create folders that we want to exclude $path = preg_split('![/\\\]!', untrailingslashit($info['name'])); if (isset($path[1]) && !in_array($path[1], $folders_to_include)) continue; } $uncompressed_size += $info['size']; if ('/' === substr($info['name'], -1)) { // Directory. $needed_dirs[] = $to . untrailingslashit($info['name']); } elseif ('.' !== ($dirname = dirname($info['name']))) { // Path to a file. $needed_dirs[] = $to . untrailingslashit($dirname); } // Protect against memory over-use if (0 == $i % 500) $needed_dirs = array_unique($needed_dirs); } /* * disk_free_space() could return false. Assume that any falsey value is an error. * A disk that has zero free bytes has bigger problems. * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. */ if (self::wp_doing_cron()) { $available_space = function_exists('disk_free_space') ? @disk_free_space(WP_CONTENT_DIR) : false;// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Call is speculative if ($available_space && ($uncompressed_size * 2.1) > $available_space) { return new WP_Error('disk_full_unzip_file', __('Could not copy files.').' '.__('You may have run out of disk space.'), compact('uncompressed_size', 'available_space'));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } } $needed_dirs = array_unique($needed_dirs); foreach ($needed_dirs as $dir) { // Check the parent folders of the folders all exist within the creation array. if (untrailingslashit($to) == $dir) { // Skip over the working directory, We know this exists (or will exist) continue; } // If the directory is not within the working directory then skip it if (false === strpos($dir, $to)) continue; $parent_folder = dirname($dir); while (!empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs)) { $needed_dirs[] = $parent_folder; $parent_folder = dirname($parent_folder); } } asort($needed_dirs); // Create those directories if need be: foreach ($needed_dirs as $_dir) { // Only check to see if the Dir exists upon creation failure. Less I/O this way. if (!$wp_filesystem->mkdir($_dir, FS_CHMOD_DIR) && !$wp_filesystem->is_dir($_dir)) { return new WP_Error('mkdir_failed_'.$method, __('Could not create directory.'), substr($_dir, strlen($to)));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } } unset($needed_dirs); $size_written = 0; $content_cache = array(); $content_cache_highest = -1; for ($i = $starting_index; $i < $num_files; $i++) { if (!$info = $z->statIndex($i)) { return new WP_Error('stat_failed_'.$method, __('Could not retrieve file from archive.'));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } // directory if ('/' == substr($info['name'], -1)) continue; // Don't extract the OS X-created __MACOSX if ('__MACOSX/' === substr($info['name'], 0, 9)) continue; // Don't extract invalid files: if (0 !== validate_file($info['name'])) continue; if (!empty($folders_to_include)) { // Don't extract folders that we want to exclude $path = preg_split('![/\\\]!', untrailingslashit($info['name'])); if (isset($path[1]) && !in_array($path[1], $folders_to_include)) continue; } // N.B. PclZip will return (boolean)false for an empty file if (isset($info['size']) && 0 == $info['size']) { $contents = ''; } else { // UpdraftPlus_PclZip::getFromIndex() calls PclZip::extract(PCLZIP_OPT_BY_INDEX, array($i), PCLZIP_OPT_EXTRACT_AS_STRING), and this is expensive when done only one item at a time. We try to cache in chunks for good performance as well as being able to resume. if ($i > $content_cache_highest && 'UpdraftPlus_PclZip' == $class_to_use) { $memory_usage = memory_get_usage(false); $total_memory = $updraftplus->memory_check_current(); if ($memory_usage > 0 && $total_memory > 0) { $memory_free = $total_memory*1048576 - $memory_usage; } else { // A sane default. Anything is ultimately better than WP's default of just unzipping everything into memory. $memory_free = 50*1048576; } $use_memory = max(10485760, $memory_free - 10485760); $total_byte_count = 0; $content_cache = array(); $cache_indexes = array(); $cache_index = $i; while ($cache_index < $num_files && $total_byte_count < $use_memory) { if (false !== ($cinfo = $z->statIndex($cache_index)) && isset($cinfo['size']) && '/' != substr($cinfo['name'], -1) && '__MACOSX/' !== substr($cinfo['name'], 0, 9) && 0 === validate_file($cinfo['name'])) { $total_byte_count += $cinfo['size']; if ($total_byte_count < $use_memory) { $cache_indexes[] = $cache_index; $content_cache_highest = $cache_index; } } $cache_index++; } if (!empty($cache_indexes)) { $content_cache = $z->updraftplus_getFromIndexBulk($cache_indexes); } } $contents = isset($content_cache[$i]) ? $content_cache[$i] : $z->getFromIndex($i); } if (false === $contents && ('pclzip' !== $method || 0 !== $info['size'])) { return new WP_Error('extract_failed_'.$method, __('Could not extract file from archive.').' '.$z->last_error, json_encode($info));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } if (!$wp_filesystem->put_contents($to . $info['name'], $contents, FS_CHMOD_FILE)) { return new WP_Error('copy_failed_'.$method, __('Could not copy file.'), $info['name']);// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } if (!empty($info['size'])) $size_written += $info['size']; do_action('updraftplus_unzip_file_unzipped', $file, $i, $info, $size_written, $num_files); } $z->close(); return true; } } Oonagh Reidy, Author at Smart Office - Page 9 of 116

    Smart Office

    Picture This: Panasonic Lumix That Applies Make Up

    Its the Lumix that makes you look luminous.


    Click to enlarge

    The camera maker’s latest release, the DMC-FP7, will lend a hand in making subjects better looking with its Beauty Retouch ‘esthetic’ [sic] and ‘face fixing’ functions.

    Its make up functionality offers pots of colours of lipstick, blusher and eye shadow and the face fixing whitens teeth and deletes unsightly wrinkles, making those pictured looking like something more like out of Melrose Place than Martin Place.

    From the pictures we viewed at the Smarthouse office, the beauty retouches call to mind the retouches made to models by magazines, giving them that eerie mannequin effect. 

    Panasonic’s nifty device also boasts a 4x optical zoom, LUMIX DC VARIO lens (35mm camera equivalent: 35-140mm), and a 16.1-megapixel high-resolution and a 9x extra optical zoom.

    Its 3.5″ LCD touch screen allows images to be dragged across the screen, making it ideal for browsing through multiple photos. Its touch zoom playback also allows users to zoom in 16x.

    Powered by its high performance venus VI engine, its intelligent resolution eliminates the graininess that results from picture noise and the flatness that sometimes comes with noise reduction. The intelligent resolution minimizes noise to produce sharp, clear images.

     

    The DMC-FP7, however, is not currently available in Australia although the Lumix FX78 which does offer the Beauty Retouch features is available here. 

    Brother, Samsung Top OZ Laser Multi Function

    Brother, Samsung and HP have all come out as top laser multifunction devices in Oz as the market grew 9 per cent last year.


    Click to enlarge

    And brands are battling it out for market share amongst small to medium business as intense discounting and product launches are on the way in 2011, IDC predicts.

    However, the results which covered the last three months of 2010 were weaker than expected, due to dropping business confidence, according to IDC’s Australia Quarterly MFD Tracker.

    However, these results were weaker than expected.

    Brands wise, Brother came out top of the pile with 32 per cent share, followed closely by Samsung (25%), HP (22%). 

     “In the second half of 2010, channels were flooded with stock as heavy pricing rebates were given during Q3 in an attempt to increase sales amongst vendors.

    Channels had excessive inventory in Q3 and softened business confidence during Q4, causing slow down in re stocking , said Cheryl Looi, hardcopy peripherals analyst for IDC Australia.

    “Not only printer-based and copier-based market leaders, such as Brother and Fuji Xerox expanded their portfolio to target the SMB segment, smaller players such as Oki and Dell did the same.”

    “Aggressive price competition and more product refreshes will be expected in coming quarters, as copier vendors attempt to increase market presence in the growing SMB segment, ultimately benefiting end users.”

     

    “SMB users should take note of the increasing options in the market to gain better value and obtain products that could better suit the different needs of their organisation,” added Looi.

    Brother    32%
    Samsung    25%
    HP    22%
    Fuji Xerox    6%
    Lexmark    6%
    Canon    3%
    Others    6%

    Price Wars: JB Hi Fi Slash Nintendo 3DS $298 & Notebooks

    It may be April fool’s day, but these prices are no joke. JB Hi Fi has just slashed the price of the newly released Nintendo 3DS to $298 and looks to be a force to be reckoned with if market analysts are to be believed.
    And Harvey’s are hot on its heels with 40 per cent off laptops.


    Click to enlarge

    JB have slashed the price of the new Nintendo 3DS to $298, cut by more than $50 charged by most other retailers including rivals Harvey Norman and Dick Smith (the RRP is $349).

    The price also pulls the rug out from under online rivals, whose lowest price is $338 from mwave.com.au, although when shipping costs are added on the total bill comes to over $350 mark.

    In fact, some online stores are charging $400 and above for Nintendo’s latest release.

    And it’s not just game consoles that are going cheap this weekend.

    Harvey Norman has cut 40 per cent off its Toshiba laptop range, including the Qosmio F60/05E Laptop, Satellite L650 and C650D. 

    According to its website, the offer only goes on until tomorrow, Saturday 2nd.

    And it’s not just consoles going cheap at JB Hi Fi, who have also cut some of its PC prices, selling Apple Macs at 10 per cent off  including the MacBook Air ($1196), MacBook Pro and iMac.

    Like Harvey’s the yellow store  also has offers on some Toshiba and Samsung notebooks.  And this price battle could intensify among the major players further as soon as Harveys brings its online trading site to life, which is set to happen very soon. 

    This comes as a report out today suggest JB Hi Fi will outperform the market rivals this year as the retail space continues its competitive jostle for market share. 

     

    “JB has one of the most successful business models in the listed retail space,” according to analyst Rob Blythe from Macquarie Investments, authors of the report.

    “It operates in a high growth market segment, is highly cash generative and self-funds its own expansion plans, driving its strong levels of growth.”

    The company said this week it forcasts net profit for the 12 months to June to be $108.5m -$113.5m, compared with the previous forecast of between $134 million and $139 million.

    This is on the back of major product releases like the Nintendo 3DS, the much hyped iPad 2 released last Friday and new TV’s by some of the major brands.

    Earlier this week, JB Hi Fi shares climbed 7% after the consumer electronics and IT company announced a buyback of much as 10 per cent of its shares.

    However, its not all sunny as margins, in the flat panel TV sector in particular, have slumped and performance of several of its Clive Anthony’s stores have been described as “lacklustre.”

    Terry Smart, the CEO of JB Hi Fi  told ChannelNews that the restructure of the Clive Anthony operation could result in some Clive Anthony stores being rebranded JB Hi Fi while others could be “closed”. 

     

    He also said that the company is looking at “store within a store” concepts for some locations with a JB Hi Fi store co-locating with a Clive Anthony store.

    Woolies Chief To Step Down

    Grant O’Brien is to take over from Michael Luscombe in September, the retail giant said today.


    Click to enlarge

    Woolworths Board has just announced its head honcho Michael Luscombe will step down in September.

    Its Chief Operating Officer of  Food and Petrol, Grant O’Brien, will be his successor and from today, will be Deputy CEO and will work closely with Luscombe until his formal commencement date on 1st October.

    Incidentally, the date will mark five years since Luscombe’s appointment as CEO, whom during his tenure achieved strong business diversification in the supermarket giant, a tripling in group earnings and sales scaling the $50 billion mark.

    He was also credited with achieving 68 percent increase in shareholder returns. “The Board is deeply appreciative of the outstanding leadership Michael has provided Woolworths Limited, particular as CEO.  His 33 years of service with the company have been nothing short of exemplary.”

    The new CEO in waiting, O Brien, 49, has broad experience working across the business and has proven “instrumental to Woolworths’ performance and future direction” including its liquor strategy and new DIY business in his 24 years with the giant, it said today.

     “We congratulate Grant on his appointment.  His depth and diversity of experience and strong strategic acumen has given him solid grounding to lead Woolworths into its next phase of growth,” said Chairman James Strong.

    O Brien will be joining Strong on the board as an Exec Director.

     

    He is the 12th CEO to be appointed from within the ranks of the company, Strong added.   

     “I am immensely honoured to receive this opportunity and would like to thank the Woolworths Board for having confidence in my ability to continue Michael’s legacy,” O’Brien said today.

    The Woolworth’s group also own electronics retailer Dick Smith and Big W stores.

    HP Send Printers To Cloud Google

    Its a first: HP ePrinters can now directly print from Google Cloud…without the need for a PC.


    Click to enlarge

    The web- and cloud-aware printers are the first to support Google Cloud Print straight out of the box, the makers said today. 

    And it also eliminates the need for a print driver or PC connection and makes it easier for users to print when anad where they want, Palo Alto based Hewlett Packard said today.

    The ePrint devices will print securely from any web, mobile, or desktop app that supports Google Cloud including Gmail for Mobile, Google Docs for Mobile and Chrome OS.
    It also plans to include other third-party apps. 

    To make the service operational, users just need to add the email address of their Photosmart, Officejet or LaserJet Pro printer to their Google account.

    “Users are rapidly migrating to web and mobile apps, and Google Cloud Print brings full-featured printing capabilities to these apps,” said Mike Jazayeri, Product Management Director, Google.

     

    “We are excited that HP has brought the first generation of cloud-ready printers to market.  While cloud printing is possible with any printer that is connected to a PC, users can achieve a more streamlined, intuitive experience by printing directly to a cloud-ready printer.”   

    Harvey Norman & Co GST Campaign A Dead Duck?

    Oz retailers more expensive than online EVEN after GST difference, Federal Commission finds. And it seems moving the threshold is more trouble than its worth.

    In other words GST is not the issue, it’s retailer’s high prices and lack of supply that is pushing consumers online, the body says.

    Lack of goods available worldwide and not sold in Australia is nothing new, although it seems consumers have now found a way around that. 

    Consumers “simply cannot purchase an equivalent product from a local supplier,” which is pushing them to look elsewhere according to findings released on Thursday.

    That is what retailing stalwarts like Harvey Norman, Myer and Solomon Lew’s Just Group have been told by the body charged with investigating their claims, that the absence of GST on online goods sold here under $1000 by foreign retailers is making trading unfair.

    “Based on the preliminary evidence available to date, it appears that even a large reduction in the threshold may not necessarily have a significant impact on the number of parcels not subject to GST and duty,” the Federal government backed Productivity Commission said in its latest findings.

    And it seems most of the goods bought online from foreign retailers are worth under the $100 mark anyway, according to Customs data.

    This puts to rest many claims by the likes of Harvey Norman, most of whose white goods and TVs would be worth well over that, and one of the original ringleaders in the GST campaign, that the loss of business to online rivals was affecting their bottom line and threatening thousands of jobs.

    “The debate has moved on,” a spokesperson from Assistant Treasurer Bill Shorten office said.

     

    The retailers campaign led by industry stalwarts including Solomon Lew, Myers’ Bernie Brooks and Gerry Harvey sought to put pressure on Canberra to introduce a 10 percent GST on all goods purchased from overseas web sites and launched a series of full page ads on Boxing Day to that effect.

    These findings will put yet another spanner in the works for the retailers campaigning for introduction of GST on foreign rivals, which received a massive public backlash at what was seen as retailers’  attempts to undermine online bargain hunters.

    After the public backlash, Chairman of Harvey Norman, Gerry Harvey, later admitted the campaign was done in “poor timing.”

    However, keeping the  GST threshold at $1000 will come at a price for Federal coffers, and would cost government $460m in lost tax revenue and up to $610m in three years time, it also found.

    The GST threshold is far higher than our neighbours NZ, at $44, as well as other nations like Canada whose limit is just $20.

    However, it would be more expensive for Government to cut the tax threshold for online purchases than would stand to collect, according to the Board of Taxation.

    Myer have already moved their online operation, called myfind.com, offshore to China in February which offers “great value and one-off special buys for online shoppers.”

     

    Despite the latest findings, it appears retailers including the Australian Retailers Association, are still seeking to pursue its campaign and have asked the government for more information.

    The Commission is now looking at other issues affecting the industry.

    Back To Black: White iPhone 4 On Back Burner

    White iPhone 4 has paled into oblivion. That’s according to reports from Apple guru websites including Geek.com.


    Click to enlarge

    The much anticipated paler model, tipped for release in June, has done a disappearing act from the Apple website, causing fans to fear the worst.

    The beleaguered iPhone 4 SKU was removed from the site displays sometime yesterday, although the other shades remain.

    White, it seems, is causing the tech giant a lot of hassle and is easier said than done.

    “The white iPhone will be available this spring (and it is a beauty!),” declared Phil Schiller, Apple’s VP of Marketing, last month.

    However, Mr Schiller was coy about which model he was referring to, making many industry speculators believe he could have been referring to the impending iPhone 5, nicknamed ‘The Terminator’ due to tis metal casing, also tipped for release on June 20.

    However,  this date could also now be pie in the sky.

    Read Metal, White iPhone 5 Out June 20? here

    However, the online removal could be just a temporary phenomena, while the tech giant fine tunes its whiter than white smartphone.

     

    So Apple lovers seeking a pale colour scheme will have to make do with a white bumper accessory which is still visible in the Apple store.

    JB Hi Fi To Open Concept Stores

    Its goodbye games and hello TVs as JB pulls a new trick out of the retail hat.The first store of its kind will be unveiled, starting with Sydney’s new Westfield centre on April 21st next and will feature TVs, computers but devoid of games or DVDs.

    And the concept outlets could also be unleashed nationally, Macquarie analysts believe.

    However, this new style of retailing won’t roll on to its mainstream stores although they may show the way to how stores will be structured in the future, CEO Terry Smart said.

     “You will get clues through that of what will make up the future JB Hi Fi stores, even though they’ll continue to have the music, movies and games content.”

    But what is the exact concept? It will be a completely devoted to home entertainment and will stock brands like Sony, Apple and Hewlett Packard, according to The Australian.

    The retailing giant has already been collaborating with major brands and fine tuning product displays. The stores will be product rather than price driven, believes one analyst.

    And it seems concept stores are all the rage. Sony has already opened its own concept store in Westfield in the US, that delivers what it says is “a complete Sony experience.” It has also said they intend to roll out worldwide.

    So, why the change of tack for JB’s?

     

    In an exclusive interview with Channel News earlier this week, Chairman Patrick Elliott said JB Hi-Fi’s online business was growing, but admitted that its “bricks and mortar” model was “very low-cost”.

    “Our cost of doing business, which is everything below the gross margin line, is about 14, 14.5 per cent of sales,” Elliott said.

    “There wouldn’t be too many online retailers who have that low cost base, and so we’re comfortable that with our scale, our buying power and our low cost to business that we can compete quite effectively with an online retailer, and to that extent clearly pushing our own online model.”

    “It will give us a chance to test what really works. But it’s one we don’t see a big opportunity to roll out into too many locations” Smart also said. 

    The giant already said this year it planned to open 23 new stores both in Australia and New Zealand, and it is not clear as to whether any of these will be ‘concept’ locations.

     

    And it’s not just bricks and mortar that has been keeping Terry Smart’s outfit busy. It is also looking at the possibility of  JB Hi Fi online appliance group as well as rebranding some of its Clive Anthony stores.

    Oz E-Stores Go ‘Mass Market’ But Can Retail Catch Up?

    2011 is set to be the turning point for retail and the industry has JB Hi Fi and Gerry Harvey to thank. Well, sort of.


    Click to enlarge

    The retail mogul is setting a good example, though, believes consultants Bain & Company, who says Harvey Norman’s recent conversion to online selling will encourage others to follow suit.

    His recent announcement “is the latest evidence that Australia’s retailers are getting serious about selling online and may “spur other companies (into) playing catch up,” it believes.

    And it’s not just Harvey’s that has shown the road to success.

    Rivals, JB Hi Fi, is one of Australia’s most successful online retail shops, with “a broad range of products and competitive prices.” The electronics giant has already said it is happy with its online performance.

    And Experian Hitwise named its e-store second-most frequented online retailer here, surpassed only by electronic giant Apple.

    Since the online market is still “relatively small” retailers have found vast investments directed to online “hard to justify,” say the consultants, since Australians still make less e-purchases than their counterparts in UK or US.

    And retailers who still suspect online may not be all it’s cracked up to be in terms of sales potential, stay tuned.

    Although in its infancy it is set to sky rocket and online sales overall will treble by 2015, to nearly $30 billion, Morgan Stanley predicts. This will only increase further with the jump in use of Smartphones and mobile tabs.

    Bain & Co’s latest survey on consumers indicates the number buying online has doubled in the past year and is reaching ” mass adoption rates” and consumers plan to up their web spend.

     

    The survey spanned all ages, incomes and locations throughout Australia.

    And according to the consumer trends that emerged,  “expect increased activity in high-penetration categories such as digital content, event tickets, travel, insurance and mobile phones.”

    More than half of consumers surveyed had bought digital content.  

    And other hot sectors look set to be books, videos or travel services online, clothing and furniture.

    So, how can retailers maximise the e-dollar? Forget about the GST campaign, for a start, warns Bain. 

    The retailers campaign being referred to was led by industry stalwarts including Solomon Lew, Myers’ Bernie Brooks and Gerry Harvey have sought to put pressure on Canberra to introduce a 10 percent GST on all goods purchased from overseas web sites, claiming it was losing trade to foreign rivals. Gerry Harvey has since backed away from the issue.

    “A more serious issue may be the prices that the owners of global brands have come to expect that Australian consumers will pay, and the strong gross margins that (Australian) retailers have come to enjoy.”

    This has also been backed up by Productivity Commission’s latest findings that “it appears that even a large reduction in the threshold may not necessarily have a significant impact on the number of parcels not subject to GST and duty.”

    More than half of those surveyed by the analysts said they shopped on overseas websites because of the price, and nearly 40 percent said that product selection or availability was the main reason.

     

    However, Australians will shop on domestic sites if the price and product is right, Bain says.  77 per cent of those surveyed purchased on Australian websites and just 23 percent on overseas sites last.

    However, retailers cant be complacent will need to invest in their online capabilities – and some may need to reduce their prices – if they are to maintain this advantage.

    Secure payments and low price were identified as top concerns followed by ease of delivery, convenience while lack of online customer service was the main sticking point.

    “Invest in a site that looks good and makes it easy for customers to find what they want, and don’t forget superior customer service online. “

    Is Nokia Free Apps ..A Cry For Help?

    It’s ‘the ‘great apps giveaway’ says Nokia. But is it a cry for help or a fantastic offer?


    Click to enlarge

    Either way, the phones giant is shouting customers who purchase its newly released C7 Smartphone free apps from its Ovi store. The apps usually cost $2 and up and it appears to be one of the only brands pursuing this strategy at present.

    However, the offer appears to be available only for customers Vodafone network who sign on for a 2 year $29 contract.

    “Add them to your phone bill and we’ll cover the cost of the apps” the ad states, which has been heavily advertised across several media outlets. It also offers access to Ovi Maps with free lifetime navigation and voice guidance.

    Nokia’s C7, comes with touchscreen 3.5 inch AMOLED high-resolution display, 8MP camera, HD video.

    The Smarthouse team  are currently reviewing the device and will return a verdict in the coming weeks.

    C7 holders can access to thousands of Ovi apps until the end of May.According to Garter research, free downloads, as opposed to free
    giveaways, will account for 81 percent of total mobile application store
    downloads in 2011 although consumers will be willing pay more as they
    become more established.

    The Finnish phone giant has been struggling for survival in the new Smartphone landscape, with rivals like Apple iPhone, Android and Blackberry all eating into its market share.

    However, its latest deal with Microsoft, which will see it adopt Windows 7 software on all its devices said to be worth $1bn to the phone giant, could reverse its seemingly slippery fortunes.

     

    Android is expected to overtake Symbian and become the number one smartphone OS in Australia within the next few months, accordingto analysts IDC, although the Finnish brand did regaining the number one place in the smartphone market late last year after being overtaken by Apple the previous quarter.

    However, this trend, was largely due to a sharp decline in device prices, say the analysts.