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; } } David Richards, Author at Smart Office - Page 57 of 91

    Smart Office

    Harvey Norman + The Good Guys Have 25% Of The Appliance Market

    Harvey Norman and The Good Guys account for one in four of all appliances sold according to a new Roy Morgan research study.

    From toasters to vacuums to microwaves to shavers, Australians spend over $260 million buying almost 1.7 million small electrical items in an average four weeks, new retail data reveals. 

    Two bricks-and-mortar retail stores dominate the category in terms of the sheer number of items sold: in the 12 months to June 2015, we bought 233,500 small electrical goods from The Good Guys during an average four-week period, and another 208,000 from Harvey Norman. Together, these retailers account for over 1 in 4 of all new small electrical items sold (26%). 

    The competition is equally fierce among the discount and department stores. Big W sells157,500 small electrical items in an average four weeks, just eclipsing the 145,500 items taken home from Kmart, while 93,500 items are bought from Target or Target Country and 73,500 from Myer.


    Click to enlarge


    Specialising in one type of small electrical item-vacuum cleaners-is enough to net Godfrey’s 50,500 sales in an average four weeks, while the only supermarket among the Top 10 retailers is ALDI with 40,500. Bing Lee and JB Hi-Fi round out the Top 10 with Australians buying 37,500 and 37,000 small electrical items respectively from those stores in an average four weeks.

    Number of Small Electrical Items bought in average four weeks at Store

    Source: Roy Morgan Single Source (Australia), July 2014 – June 2015, n=14,987 Australians 14+

    Over 1 in 10 small electronic items are now purchased online (179,000 during an average four weeks). Of these, we buy 134,500 items through online-only stores such as Appliances Online and Deals Direct. The remainder are purchased via the online sites of bricks-and-mortar retailers.

    In an average four weeks, Australians buy 57,000 small electrical items through online auction site Ebay-by far the largest individual online retailer in the category.

    Over 400,000 more small electrical items are bought during a four-week period from other bricks-and-mortar stores, with individual sales volumes below the top 10 above-equivalent to around 25% of the overall market. These include Betta Electrical (27,000) and Dick Smith Electronic, as well as Bunnings (33,000) and other hardware stores, plus other supermarkets, other discount and department stores, and smaller niche retailers.

    Andrew Price, General Manager – Consumer Products, Roy Morgan Research, says:

    “Australians buy almost 1.7 million small electrical items during an average four weeks, spending an average $154 per item. 

    “While the average small electrical item bought from a discount store such as Big W, Kmart or Target costs $72, it’s over $200 at market leaders Harvey Norman and The Good Guys. So while these two sell 26% of all individual small electrical items, these sales represent 37% of the market by dollar value, or over $96 million in an average four weeks between them.

    “Overall, Australians spent around $27 million on small electrical items online in an average four weeks, whether through Ebay, an online-only retailer or the internet site of a bricks-and-mortar store. This represents only around 10% of our total spend in the category. Unlike items such as clothing and books, it can cost too much for consumers to ship small electrical goods from overseas-and in many cases they can’t even be plugged in here. This has made the category safe from international sites such as Amazon.”

    Is Dick Smith Set To Be Another Clive Peeters? As Share Value Plunges 85%

    Consumer Electronics suppliers to Dick Smith whose shares plunged to $0.32 cents today, are seriously concerned that the mass retailers are set to become another Clive Peeters or WOW Sight and Sound, two consumer electronics retailers who went bankrupt leaving vendors out of pocket.

    Just after the ASX opened today shares in Dick Smith plunged more than 50 per cent to 32?. They started the year at$2.29.

    Last month Dick Smith CEO Nick Aboud forecast a 2016 net profit between $37 million and $43 million, compared with earlier guidance between $45 million and $48 million and last year’s net profit of $43.4 million.

    The shares plunged from $1.30 to $0.75.

    Today they took another dive with several distributors now telling ChannelNews that they are reluctant to supply the mass retailer unless they have “cash up front” or a “bank guarantee”.

    ChannelNews has been told today by insiders, that the retailer is struggling to meet internal sales and profit targets and that even the numbers forecast by Aboud back in October will not be met.

    Senior store management said that Dick Smith stores are not attracting floor traffic “anywhere near” what JB Hi Fi, The Good Guys and Harvey Norman are getting running into the peak buying period.   

    The catalyst that triggered today’s decline was a statement to the ASX indicating that the consumer electronics retailer slashed the value of inventories by at least $60 million and the stock is now down 85 per cent since the start of the year and has a market capitalisation of just $74.5 million.

    Dick Smith launched an inventory review after a 4 to 5 per cent slump in same-store sales in October prompted managing director Nick Abboud to step up discounting and marketing and warn that profits could fall as much as 15 per cent this year as gross margins came under pressure.

    Mr Abboud said on Monday that November trading was also below expectations, stock holdings remained too high and the company was “unable to re-affirm the profit guidance previously provided”.

    Mr Abboud said the inventory review was still underway, but Dick Smith’s board had decided to book a non-cash impairment charge of $60 million before tax and further impairment may be required, depending on Christmas trading.

    Earlier this month ChannelNews exclusively tipped that the Company had inventory problems and that revenues were falling. 

    “We remain cautious on the outlook for the Christmas trading period. We will continue to drive sales, maintaining flexibility on gross margin to reduce inventory and improve our net debt position.” Aboud said today. 

    The statement issued to the ASX today read:

    The inventory review is being conducted with the assistance of external consultants and is aimed at determining the appropriate size of various categories, improving the depth and breadth of inventories, and identifying the level of marketing support to achieve inventory cover targets.

    Dick Smith management claim that they want to boost its “share of voice” and has flagged deeper discounts on brands such as Apple and Fitbit.

    A Harvey Norman executive told ChannelNews, who will pay for this. “Dick Smith can discount all they like but if manufacturers and distributors stop supplying them they will have no stock to discount. There is also the issue of whether they will be in a position to pay vendors after they have discounted stock running into Christmas and the New Year”.

    They Added “Discounting by Dick Smith could have an impact on other retailers but I doubt it because very few people are actually walking into their stores”.

    Another major retailer said “If they do move to rampant discounting they are more likely to appeal to online shoppers and this is still only a small part of the total consumer electronics mix.

    “They are heading to become another Clive Peeters or WOW Sight + Sound both these Companies went broke after heavy discounting. WOW Sight + Sound who while being Queensland based did exactly what Dick Smith are doing by discounting heavily running into Christmas and then declaring after Christmas that there was not enough money around to pay their bill so they went broke”.   

    New Toshiba KIRA Offering, Is It The Best Ultrabook Yet?

    A pragmatic interpretation of what constitutes quality especially in an Ultrabook is a subject that is open to multiple benchmarks. Is it design, components, after sales service, components, speed and performance or the quality of a display screen versus what were past industry benchmarks?During the past decade very few mass notebook

    manufacturers have been able to differentiate their notebook offerings. Many had

    bog standard chipsets, motherboards and the only point of difference was the

    casing that the common components were housed in, the one exception being Apple

    and their MacBook range.

    Recently Toshiba took a massive punt with the launch of a

    new premium brand called KIRA and one of the first products delivered

    under this new premium brand is an i7 Ultrabook (PSU7FA-00T00K). It comes with

    a display screen that delivers the best display I have ever seen in a

    notebook or Ultrabook, whilst the minimalist design includes a power on button with

    almost no LED lights other than a Wi Fi indicator on the front of this PC.


    Click to enlarge

    Smooth rounded edges and brushed aluminium is all part of

    the quality look that cocoons this device.

    If there was ever a PC company that can make premium quality

    PC components it is Toshiba.

    A Japanese company which delivered the first ever notebook, Toshiba has the engineers and the technical skill to deliver breakthrough

    technology and it shows with this new offering.

    Miniaturisation and making things work in small form factors

    is one thing Toshiba does well.

    Their Android tablets were the first wafer-thin devices to

    deliver not only a great look and feel but functionality such as built-in HDMI

    and USB attach capability. Even Samsung struggled to deliver the capability that

    Toshiba was able to achieve in their tablets.

    Now the company has delivered a premium Ultrabook range that

    sets a new benchmark in the space.


    Click to enlarge

    Weighing in at 1.28kg, this Ultrabook is just 20mm thick or

    23mm if you count the rubber mounts on the bottom.  

    In the hand the first thing you notice with their new i7 touchscreen

    Ultrabook is that it has a superior look and feel and when you open the lid

    what bursts into life is a display screen that packs 2560 x 1440 pixels into 13.3-inches.

    Running a video is a sheer delight as the vivid colours

    scream from the display and the sharpness is as good as any HD TV.

    For some this screen could present a problem. Firstly, it

    delivers a superior resolution which when it comes to using the device for

    standard PC functionality such as emails, writing a report or working on an

    Excel spreadsheet, is far superior to what is needed.


    Click to enlarge

    Icons appear small on the screen and text is reduced, especially if you have the display screen set to maximum resolution. All you

    have to do to overcome this problem is go into your display setting and set

    your screen at a lower resolution.

    For movies and video you can easily reset the resolution.

    One great idea would be for Toshiba to build in a button that allows users to

    toggle between work mode and movie mode. Toshiba has installed a program called

    Desktop Assist that allows you to enlarge text by up to 200 per cent when you

    use the custom settings. This did not work for me as I am a big Chrome user and

    the text appeared way too large.

    Videos delivered at lower resolution appear normal but they lack the real sharpness that

    the 2560 x 1440 pixel display screen is capable of delivering.

    A key advantage of the higher resolution is that you can

    pack more in when operating in side by side mode with two pages easily

    displayed to the KIRA screen.

    As someone who spends hours on a keyboard I found the

    Toshiba keyboard extremely easy to use, the spacing between keys is right and

    the key size is excellent. Another big advantage is that the keyboard is

    backlit so working on an aircraft at night or at home in low light is easy.


    Click to enlarge

    Under the bonnet is a Core i7 CPU which screams along and Toshiba

    have to be complemented for getting this processor to work in such a small

    package. It is also available with an Intel Core i5 processor.

    The Core i7-3537U in the version that SmartHouse reviewed

    ran at 2GHz, and was one of the fastest we have ever experienced in an

    Ultrabook.

    The casing is made out of pressed magnesium alloy that has a

    honeycomb structure, this design add strength while keeping the weight down.

    The hinge is extremely strong and the instant open design allows one to easily

    boot this Ultrabook in seconds. The rigidity of the honeycomb design and the

    strength of the hinge which I believe is a key component in any notebook or

    Ultrabook design has allowed Toshiba designers to deliver a truly superior

    structure while not compromising on the design or the weight of this Ultrabook.

    A power button and an HDMI and two USB ports are located on

    the left side of the casing. On the right side is a full-sized SD card slot and a

    third USB port.

    There is no Ethernet port, with users having to rely on the

    built-in 802.11ndual-band Wi-Fi. There is also Bluetooth version 4.0 built-in, along with a 2.0 megapixel webcam. An Ethernet dongle is optional.

    Storage comes in the form of a 256GB SSD drive and 8GB of

    RAM.

    Where this device was a real knock out was in Ultrabooks

    performance. I took the device on a three-day trip to Melbourne and after a full

    day of use delivering PowerPoint presentations, doing emails and Skype calls it

    still had juice left in the battery.

    We also ran the screen flat out running a movie and two

    additional videos and the Ultrabook delivered 4 hours and 50 minutes of battery life

    before it was screaming for a power top up.

    A small drawback is the fan which can be noisy when it is

    spinning at full belt, something that Toshiba needs to work on.

    The touchpad is large (105x59mm) and I found it easy to use, however I would have preferred it to run flush across the brushed aluminium as

    opposed to having a stainless steel ridge; a change in colour would have done the job.

    The touchpad supports Windows 8 swipe-in gestures.

     


    Click to enlarge

    Another big standout on this Ultrabook is the inclusion

    of  Harman/Kardon speakers which when you

    think about it have to be powerful because of the size. They don’t only deliver

    excellent sound despite the low frequency but are also discreetly housed under the

    Ultrabook, with the speakers pointing out to the sides.

    Conclusion

    At $2199,  this is an

    expensive beast. But is it worth it? My view is yes for the simple reason that an

    Ultrabook of this ilk is going to get used and the last thing you want if you

    use an Ultrabook every working day is one that cannot last the test of time and

    the constant thrashing that a lot of Ultrabooks get.

    What you get is quality construction, a blindingly stunning

    display screen and a device that looks and feels classy. If you rely on a

    notebook as I do to deliver day in and day out, it’s going to be very had to go past

    this beast, especially as Toshiba has thrown in a superb support package.  

    It includes an on-site two-year warranty for capital cities, with regional owners getting a pick-up

    service. The package also covers accidental damage and a dedicated

    phone line for support. It also ships with the Pro version of Windows 8.

    On the downside Toshiba needs to work on the cooling fan and

    the problems associated with the sheer resolution of the display screen, though neither of these small issues detract from the fact that this is the best

    Ultrabook we have ever seen.

     

     

    Width: 316mm

    Depth: 207mm

    SOFTWARE

    Operating system Windows 8 Pro

    Included software Adobe Premiere Elements 11 Adobe Photoshop

    Elements 11

    STORAGE

    Hard drive interfaceSATA

    Hard drive type Solid-state drive

    Internal storage256GB

    Solid-state drive? Yes

    Supported memory mediaSD

    WARRANTY

    Warranty information on-site, accidental damage

    Warranty length (months): 24

    WEBCAM

    Webcam resolution (megapixels): 0.9

    WIRED CONNECTIONS

    Number of USB 3.0 ports:3.0

    Wired Terminal/PortsCombination headphone/microphone,

    HDMI, USB 3.0

    WIRELESS CONNECTIONS

    Bluetooth: Yes

    Wi-Fi (wireless networking): Yes

    Wireless technology supported Bluetooth, Wireless 802.11a,

    Wireless 802.11b, Wireless 802.11g, and Wireless 802.11n

    Sony Ericsson’s Dumb Phones

    There may have been plenty of rumours about a dumbed down version of the P990 smartphone, but instead Sony Ericsson has chose to launch a trio of new phones aimed at basic consumers.

    The new handsets are the company’s answer to the Vodafone Simply phone which the network launched in collaboration with Sagem earlier in the year. Largely bereft of features – there’s no camera, MP3 player or email – they are designed so that even the most tech-unsavvy users can find their way around them. The phones are expected in Australia early next year.


    Click to enlarge

    Sony Ericsson’s big idea, for two of the phones, the J220 and J230, is to include an option that allows the user to customise the interface to make it even simpler to use. They can either opt for the traditional multi icon interface or go for a single icon menu. This enables them to access their core features – and on these phones there aren’t a great deal to chose from anyhow – with one click.

    The chocolate bar style handsets also include a 128×128 pixel, 65k colour display, embedded games and a calendar and the J230 has an FM radio, but that’s all. The pair have a battery standby life of 280 hours. Even more basic is the Z300, a small clamshell with fewer features than its siblings. It doesn’t even sport the speaker phone that’s found on the other two new phones and there’s no single icon user interface option. It does however feature a 128×128 pixel, 65k colour display, embedded games and a calendar.

     

    Is David Jones Set To Pull The Pin On Dick Smith, Some Say Yes?

    Dick Smith’s move into appliances could be timely with David Jones tipped to give the mass retailer the boot from their stores.

    ChannelNews has been told that Dick Smith is trying to renegotiate the extension of a three year contract with David Jones which is due to run out in October 2016 despite their being a three year option in the contract. 

    A key element of that contract that saw Dick Smith set up consumer electronic stores inside David Jones stores was a none compete clause that prevented Dick Smith from selling appliances which is a major category for the now Woolworths owned stores. 

    In an interview early week, David Jones chairman and Woolworths Holdings chief executive Ian Moir declined to comment on rumours that both sides wanted out.

    Nick Aboud the CEO of Dick Smith has played down speculation that Dick Smith’s concession-style agreement with David Jones could come to an end before the initial three-year term comes up in October 2016.
    “My meetings with David Jones have been about how we improve the business for both parties – that’s been the conversation I’ve had with them,” he said yesterday. “Over the next 12 months it’s about how we improve both businesses and we’ll look at it in a year’s time.” he told the Fairfax Media.


    A senior David Jones executive told ChannelNews that the contract will not be renewed due to “Other strategies” currently being worked on by the department store. 

    Woolworths Holdings does not sell consumer electronics in South Africa and is believed to be keen to use the space currently occupied by Dick Smith in 29 of its 38 stores for other categories.

    What is not known is whether David Jones will use their NARTA relationship to move back into selling selected consumer electronics. 

    The Financial Review said recently that Dick Smith was initially keen to tap into David Jones affluent A-B demographic and American Express customer, but is now generating stronger sales growth from its stand-alone and Move stores.

    Under the deal, Dick Smith agreed to pay David Jones a fixed percentage of sales every month, and make a minimum payment each year regardless of sales volumes. The department store stood to make even more from the arrangement if Dick Smith’s sales grew.

    Push could come to shove later this year, when David Jones sells its Market St store to developers. Market St houses the largest David Jones Powered by Dick Smith concession.

    LG Reports A Strong Result Profits And Revenue Up

    LG Electronics ,who claim that they are the #1 consumer electronics brand in Australia, have reported higher quarterly sales and profits due to strong mobile phone and flat-panel TV sales. In their latest report sales have jumped 22.1 percent to $12.534 billion and operating profit was $843 million, which results in a 6.7 percent profit margin for its fiscal second quarter, ended June 30.

    One of the strong performers was the LG  mobile communications company which posted  record sales of $3.788 billion, up 34.3 percent from the second quarter 2007. From handset business, sales reached $3.695 billion, up 38.6 percent from a year earlier. Operating profit margin improved to 13.9 percent, 14.4 percent in handset division due to growth in high-end models and improvements in operational efficiency, the company said.

    However analysts are forcasting that the Companies mobile phone operations have peaked and will be impacted by the new Apple 3G iphone.


    Shipment of handsets also recorded the highest in unit sales as well, a total of 27.7 million mainly from North America (33 percent) and emerging markets including Middle East Asia, CIS and Central & South America, thanks to strong sales of “Secret,” “Viewty,” “Venus” and other premium phones.


    Sales in the digital appliance company increased 4.9 percent to $3.721 billion year-on-year, and operating margin was 7.2 percent despite challenging business environment such as higher raw material prices, economic slowdown and U.S. market contraction from sub-prime effect.


    Sales in the digital display company jumped 37.2 percent to $3.683 billion, powered by a rise in sales of flat TVs, with an 86 percent increase in LCD TVs and 31 percent in plasma TVs from a year earlier. The PDP module sales grew 22 percent. Following profitability turnaround in the previous quarter, operating profit in the second quarter successfully remained profitable at $37.4 million, the company said.


    Sales from the digital media company was 2.1 percent lower on year, to $1.201 billion, due to low seasonality, but operating profit and margin increased to $12.8 million, or 1 percent, by a strong effort for cost innovation, LG noted.

    EXCLUSIVE:Fighting Breaks Out Between Management At Dick Smith As Sales Slump

    Fighting has broken out between management at Dick Smith after store staff accused the Companies online team of “cannibalising store sales.

    Senior management from the struggling retailer were told during an internal conference call that store traffic at the struggling retailer was down, store management responded by blaming the impact of the Companies online operation. 

    Dick Smith CEO Nick Aboud told managers that “customer traffic is no excuse for stores not performing in sales”.

    ChannelNews understands that as of 25th of October internal sales budgets were down over $100 Million, while internal profit targets were down $26M.

    Aboud who has been personally driving the online sales operation, told managers that their claims were “smoke and mirrors” he “blamed the teams in stores for letting the company down” a comment that did not go down well with senior store managers who are struggling to grow their business. 

    According to sources Dick Smith wants online to make up 15% of their overall sales operation going forward. This claim management will take the pressure off the Companies looming cash flow crisis with distributors set to be used to fulfil orders for Dick Smith’s online operation.

    Both buyers and merchandising executives at Dick Smith, have told ChannelNews that there are real concerns over the Companies house brand strategies with senior executives claiming that a lack of “experience” in the Companies Hong Kong based house brand sourcing office, had led to major problems for buyers who they claim were not consulted about stock being shipped into stores.

    The General Manager of the Hong Kong Office for International Sourcing at Dick Smith Electronics is Tony Abdel-Ahad.

     Abdel-Ahad, joined Dick Smith from Abu Dhabi based Mezzo Middle East two years ago which was prior to the Company being floated. 

    Mezzo is a Middle East furniture Company, prior to that he worked as a regional director for Asteco a Dubai based Middle East real estate Company.
    He appears to have no prior electronics buying or consumer electronics or appliance expertise. 

    Insiders claim that he is related to a senior Dick Smith Director.

    As of yesterday shares in the Company had slumped to $0.775 after trading earlier in the year at $2.29.

    Last week shares in Dick Smith who is believed to be facing cash flow problems running into the peak buying period fell over 35% several institutional investors dumped their shareholding in the Company.  

    Earlier this week we revealed that two more senior merchandising managers have quite the embattled retailer. This follows the of loss Rod Orrock the Director of Buying and Marketing at Dick Smith who quit 14 months after leaving a senior role at Harvey Norman to take a role at the mass CE retailer.

    Orrock said that he left due to “ill health”.

    EXCLUSIVE:Chinese House Brand Supplier Put In Bid For Dick Smith

    A new Chinese bidder has emerged for Dick Smith.

    The new bidder is Chinese Company Shenzhen MTC who supplied Dick Smith with over $150M dollars’ worth of house brand TV’s last year. 

    Still in negotiations for the retailer is Indian conglomerate Tata who also owns consumer electronics manufacturing plants in India, including a TV manufacturing plant who is currently looking to buy into Vizo one of the biggest suppliers of TV’s in the USA. 

    Shenzhen MTC was established in April, 2005. 

    A public Company registered on the at Shenzhen Stock Market, the Company manufactures consumer electronics at three factories in Fuyong, Shajing and Songgang in Shenzhen. 

    The Company only made contact with Dick Smith receiver Ferrier Hodgson last week.

    According to sources the man who bought Shenzhen MTC into the bidding process is Tony Abvel Ahead a relative of former Dick Smith CEO Nick Aboud who is still listed on LinkedIn as the General Manager of International sourcing for Dick Smith.

    Abvel Ahead who was responsible for sourcing and shipping millions of dollars’ worth of Dick Smith house brand stock, that is still sitting in warehouses in Australia including 3 years supply of batteries has been involved in the negotiations with Shenzhen MLC.

    Former managers at Dick Smith have told ChannelNews that the close relationship between Aboud and Abvel Ahead was often questioned at the Company due to the “excessive” level of house brand orders being placed on the Dick Smith Hong Kong subsidiary run by Abvel Ahead.

    ChannelNews has been told that management from Shenzhen MTC are looking to recruit Australian retail management including some former Dick Smith management to run the retail operation. 

    TEAC Sales Plunge Distributor Reports $5.7M Loss

    TTA Holdings the distributor of the struggling TEAC brand that has been available in Australia for more than 36 years has reported a 46% decline in revenue and losses of over $5.6M during the last financial year.

    Back in January the CEO of TTA Holdings James Phoo, quit with the Company elevating Daniel Seow the senior product and marketing manager to run their operations in Australia.
      
    The Company said that TTA’s revenue had decreased from $51.1million in 2014 to $27.6million in the year ended 31st March 2015.

     This resulted in an after tax loss of $5.6million compared with a profit of $464,000 in the previous period. 

    This loss was largely attributed to incentives paid to mass retailers to clear excessive stock.

    A write down of goodwill of $2million from $2.92million to $900,000 also contributed to the result.

    ChannelNews understands that one major retailer is set to delist TEAC audio products in their stores. 

    In an effort to improve revenues the group took on the distribution of additional brands including iDance and TEAC car radios. 

    The current visual products range includes flat panel televisions, (LED, LCD), portable DVD players, digital set top boxes and twin tuner personal video recorders. 

    Audio products include sound systems, potable digital/analogue audio, iPod/iPhone docking systems and clock radios. The iDance products include DJ (disk jockey) products, headphones and party sound boxes. The Group’s sales are primarily from the distribution of audio visual products.

    Currently TEAC products are distributed through major retailers including Harvey Norman, JB Hi Fi, Good Guys and BSR (Betta Home Living). 
    Other home State based retailers such as the Radio Rental (South Australia) Group in South Australia and Retravision and Kambos in Western Australia also form part of the Companies distribution network.

    In their annual report the Company said that major restructuring process has taken place over the past year to reduce costs in line with the reduction in revenue. This resulted in several senior and operational staff being retrenched. 

     As part of the process, interstate sales and show room offices were amalgamated back to head office in Melbourne. 

    They said that the Group’s inventory level has been significantly reduced to 45 days stock turn as compared to 90 days in the previous period.

    Executives said that the Group is finding the Australian market “challenging” and that a weak Australian dollar was driving competitive price competition between major brands a move that was hurting the Company.

    TTA said that their forward strategy is to consolidate its product range and focus on product profitability and higher turnover to deliver improved results. 

    Support from major retailers to continue ranging the struggling product range is critical to future results the Company said.