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 54 of 91

    Smart Office

    Harvey Norman Ask And Get 45 Days Credit

    Harvey Norman is pressuring vendors to change their trading terms to 45 days. In the past they have operated on 30 or under terms depending on their relationship, discounts and rebate volumes insiders have told SmartHouse.

    This week the Company combined the buying offices of Harvey Norman, Domayne and Joyce Mayne with several buying staff being retrenched.


    Under pressure from the new 45 terms is Australia’s largest distributor Ingram Micro who has recently announced that they have cut 16 back of house credit management staff.


    Ingram Micro Australia who operates on wafer thin margins some as low as 2% are now being forced say management to accept 45 days instead of 30 in a move that one insider told ChannelNews was having a big impact on cashflow.


    A major vendor who has a multimillion dollar exposure to Harvey Norman said ” the bottom line is that when a partner like Harvey Norman want extended terms we have to borrow more money if we decide to extend their credit terms. Several retailers are under pressure and one way that they are preserving cashflow is by negotiating new credit terms. Others are cutting back on stock levels however this has hurt some retailers. For example in December and January we saw demand for flat panel TV’s rise however some retailers suffered because they did not have stock or extended credit terms”.


    One retailer who has admitted that a lack of stock hurt their operations was Clive Peeters they have also said that in recent months that they have cut back on stock levels in an effort to preserve cash flow.

     

    One major consumer electronics retailer said the issue for Harvey Norman is not consumer electronics but everything else. “They have a lot of stock in their warehouses from furniture and bedding to soft furnishing and because this merchandise is not selling they have a potential cash flow issue.”.


    “In reality they will be pushing their terms out to 60 days. With many retailers who are not publically listed the insurance Companies who are providing credit cover for vendors are going to be reluctant in this environment to support extended credit terms” they said.


    A look at JB Hi Fi credit terms shows that currently they are trading on a 56 day cycle from the placing of an order to the payment for the product.

    David Ackery General Manager of Electrical at harvey Norman said that he was unable to comment on the issue of credit terms.

    Toshiba Shares Smashed After $1.2 Billion Dodgy Profits Scandal

    Struggling PC, copier and medical equipment Company Toshiba who last year slashed 19 PC operations around the world has admitted they have been fudging their profits for several years.

    Now the Japanese Company is set mark down past profits by a massive $1.2 billion due to what Toshiba management are describing as “accounting irregularities”.

    The latest markdowns is almost double its earlier estimation with a large percentage of profits or lack of them attributed to the poor performance of the Companies PC Division. 

    In a case shaping up to be one of corporate Japan’s biggest debacles since a scandal at Olympus in 2011, a Toshiba-appointed independent panel has been investigating the infrastructure giant’s finances after Japan’s financial watchdog warned of possible accounting irregularities back in February.

    The company said in May it would need to reduce its profit for the 2009 through 2013 fiscal years because of improper accounting.

    Since the problem was discovered and following investigations by independent investigators, massive irregularities were discovered in Toshiba’s personal computer and semiconductor businesses

    Initially Toshiba management tried to hide the scale of scandal.

    The independent panel is expected to report its findings by mid-July. Chief executive Hisao Tanaka has hinted there will be a major management reshuffle at that time with the real possibility emerging that more Countries selling PC’s could be slashed or the PC Division dumped all together.

    The Wall Street Journal said that as a result of the accounting problems, Toshiba hasn’t been able to report its earnings for the 12-month period ending March 31-an announcement originally scheduled for June. The company hopes to file the earnings by the end of August.

    Worries over the earnings restatement and the company’s corporate governance culture have weighed on Toshiba’s shares which have plunged on the Tokyo stock market. 

    Analysts say, however, they are not worried about the possibility of a cash shortage.

    “This is one of the largest crises in our company’s history,” said Mr. Tanaka, at an annual meeting of shareholders last month. Some shareholders demanded the resignation of all executives when their terms expire in September.

    Amazon Is Coming & Retailers Had Better “Be Ready” Warns Officeworks Boss

    Amazon is coming to OZ and retailers had better be ready.

    At a Retail Leaders forum in Sydney yesterday, Wesfarmers group managing director Richard Goyder said that the US Amazon giant will “eat all our breakfasts, lunches and dinners”, unless Australian retailers become more innovative and barriers to competition are removed.

    Wesfarmers owns both Coles, Officeworks, Target, Bunnings + K Mart. 

    At CES 2016 a Best Buy executive told ChannelNews that the Company had to “learn quickly” how to deal with Amazon when they started stripping market share away from the consumer electronics retailer. 

    Greg Revelle the chief marketing officer for Best Buy said that the retailer had to make sure that they delivered a better instore experience while competing head on online. 

    A former senior executive at Radio Shack said that it was the impact of Amazon that killed off Radio Shack.

    Mr Goyder said that Amazon are currently are gearing up for a major push into Australia and that retailers had better be ready. 

    Goyder says his biggest fear was not the competitive threat from Australian retailers but overseas online retailers such as Amazon. 

    He said that online retailers such as Amazon, Google and asos.com could trade 24 hours a day, seven days a week and 365 days a year, while Australian bricks and mortar retailers, including Wesfarmers’ Bunnings, Kmart, Target, Coles and Officeworks chains, were restricted by archaic trading hours and excessive regulation.

    When it was first founded in 1994, Amazon.com was operating out of founder Jeff Bezos’ garage and earning $20,000 per week in sales within two months. Twenty-three years later, Amazon has 175,100 full-time employees and earned nearly A$150 billion in revenue in 2015.

    Known as ‘The Everything Store’, especially with Amazon Marketplace enabling sellers to offer new and used items alongside Amazon’s own products the launch of Amazon could have a major impact on Australian retailers who lack capability.

    The US Company is already sourcing warehouses and several major Australian Companies are already using Amazon storage and cloud serices offerings.  

    Amazon began as an online book retailer, thanks to Bezos’ research that illustrated the worldwide demand for literature combined with the low price of books and the huge number of titles available in print. Gradually, Amazon expanded into selling CDs, DVDs, toys, electronics, and appliances.

    After surviving the dot-com bubble bursting at the start of the 21st century, Amazon introduced Amazon Prime in 2005, a membership offering free two-day shipping on all eligible purchases for an annual flat-rate fee. Later Amazon Prime expanded to offering its members streaming media. In 2007, Amazon launched Amazon Kindle, an e-book reader, followed by a tablet computer called the Kindle Fire in 2011.

    Amazon Prime, meanwhile, has also contributed dramatically to the company’s growth. In 2009, Amazon had 2 million Prime members, and in 2011, that grew to 5 million; in 2015, Prime membership is estimated to be around 40 million, and worldwide paid membership grew 53% last year. Even better for Amazon? Prime customers spend over double what non-Prime customers do- about $1,500 per year compared to about $625 per year.

    One of Amazon’s most significant lasting impacts was their adoption of “free shipping” in the early 2000s, when they began their Super Saver Shipping program, offering free shipping for orders of over $25. During one promotion, Amazon began offering free shipping with the purchase of a second book, which lead to a huge jump in sales. Clearly, Amazon realized that consumer psychology favored labeling things as “free”; however, they might not have foreseen the effect free shipping would have on side industries such as shipping and logistics.

    Some retailers doubt that Amazon will be able to offer free shipping in Australia due to the high cost of transport. 

    The Impact of Amazon.

    A consumer realizes that he needs to replace his coffee maker. 

    He knows he has two choices: driving to a physical store and hoping that they have the model he wants, and loading it into his car and driving home, or searching online until he finds his preferred make and model at the lowest price available, and having it shipped directly to his home. Which one does he choose? These days, our hypothetical consumer is becoming much more likely to choose the latter option. Thanks to online shopping, foot traffic, retail space openings, and holiday sales in retail stores have all decreased significantly in the USA 

    When and if Amazon arrieves in Australia the impact on retailers could be “severe” especially with foot traffic through the door of retailers claims analysts. 

    For example, brick and mortar retail stores in the USA saw foot traffic cut by about 50% between 2010 and 2014, from more than 30 billion visitors during the holiday season to 17.6 billion visitors. 

    Similarly, new retail space openings have been extremely low compared to years past, down to 43.8 million square feet in 2013, compared to more than 300 million in 2000, 2005, 2006, and 2007. 

    Finally, online sales increased at more than double the rate of brick and mortar sales during the 2015 holidays; in fact, Shorr research saw a 35% increase in business with e-retailers during the 2014 peak holiday season. 
    These trends have led to significant drops in revenue for some companies, and in some cases, even bankruptcy, with large companies such as Radio Shack, Circuit City and K-B Toys experiencing total liquidation. And while the US economy has added around 2.4 million jobs since the Great Recession, the retail industry has experienced a net loss of 60,000 jobs since the recession.

    New BenQ Lineup

    BenQ has announced several new products at the 2006 CES Show. They include new LCD TV’s & Projectors.

    BenQ has announced a new 42-inch LCD TV model  at the 2006 CES show in Las Vegas.

    Priced at US$2,999, the new model provides a contrast ratio of 1000:1, a resolution of 1920_1080 and a 8ms response time.The PL4271 features what BenQ claims to be its proprietary “Senseye” digital-enhancement technology, which automatically and dynamically improves image quality, adjusts brightness and darkness levels, performs color mapping procedures and reduces jagged edges on images.

    BenQ will also launch two new entry level LCD TVs, the 32-inch VL3231 and the 37-inch VL3731 in the second quarter of 2006 with price tags of US$1,299 and US$1,799, respectively.

    The Company also showcased a full lineup of digital projectors. The range includes four new models the CP120, MP770, MP720p and the PE8720. The CP120 is being called the “world’s smallest and lightest micro-portable digital projector” by BenQ. This model comes packed with built-in 802.11a/b/g wireless capability and weights only 2.9 pounds, which makes it an ideal solution for mobile professionals, according to the manufacturer.

    The CP120 boasts a 2000:1 contrast ratio and is compatible with most major video standards, from NTSC and PAL to EDTV and HTDV formats, including 1080i, 720p, 576p, 576i, and 480p. More specs below:


    8.58″ (w) x 6.77″ (h) x 2.40″ (d) unit dimensions
    2.9 lbs weight
    DLP&153;-based chipset
    IEEE802.11a/b/g wireless presenter
    2000:1 contrast ratio
    5-segment Golden Ratio Color Wheel
    4:3 aspect ratio with 16:9 support
    XGA native resolution
    54 inches at 6.6 feet throw ratio
    1500 ANSI lumens brightness
    Inputs: NTSC, PAL, SECAM, EDTV and HDTV (1080i, 720p, 576p, 576i, 480p and RGBHV)
    The unit is available now for $1,799.

    Next up is the MP720p, designed for education and business professionals. The portable projector is even more value-attractive with a price tag of $1,299. The unit is also available now. Its specs, as follows:


    11.45″ (1) x 3.70″ (h) x 9.21″ (w) unit dimensions
    6.6 lbs weight
    Full 16.7 million color palette
    2000:1 contrast ratio
    5-segment color wheel
    4:3 aspect ratio with 16:9 support
    XGA native resolution
    2500 ANSI lumens
    Inputs: NTSC, PAL, SECAM, and HDTV (480p, 576p, 720p, and 1080i)
    The MP770 is being billed as a solution for classrooms. It will be available in Q2 of this year with a price tag of $1,999. Its specs below:


    Technical Specifications
    7.17″ (l) x 3.22″ (h) x 9.21″ (w) unit dimensions
    6 lbs weight
    Full 16.7 million color palette
    2000:1 contrast ratio
    5-segment color wheel
    4:3 aspect ratio with 16:9 support
    XGA native resolution
    3200 ANSI lumens
    Inputs: NTSC, PAL, SECAM, and HDTV (480p, 576p, 720p, and 1080i)
    And finally, BenQ’s cream of the crop HD projector, the PE8720, has been created with high-end videophiles in mind. The unit costs $7,999 and is available now. Its meaty specs are listed as follows:

    TI Darkchip3 DMD
    Brightness: 1000 ANSI-lumens
    Contrast Ratio: 10000:1
    Input terminals: Component (BNCx5, RCAx3), S-Video, Composite (RCAx1) and HDMI (with HDCP x1)
    Weight: 9 kg (20 lb)

    No Australian pricing is yet available.

    Overseas Retailers Take To Kaiser Baas Adventure Tech Products

    Kaiser Baas is a name that was created early in 2006, it sounds European, but it’s only now that retailers in the UK, New Zealand, Europe, Asia and the Middle East are flocking to stock the Companies adventure tech consumer electronics products.

    The reality is that the name and the Company behind Kaiser Baas is 100% Australian right down to their action cameras, drones, crash cameras and media centre boxes which are scoped packaged and designed in Melbourne. 

    In recent weeks Dixons’ in the UK, Curry’s, Harvey Norman in Ireland and distributors such as Computers Unlimited have moved to sell Kaiser Bass branded products that very soon could include a Sedgeway type product with no handles. 

    Also set to stock the Kaiser Baas product range is UK retailer Maplin.

    Behind Kaiser Baas is a management team who know how fickle consumer electronics distribution can be.

    Evan Kourambas the CEO of Kaiser Baas has been around the IT channel for decades. 

    Prior to creating Kaiser Baas he ran Lako Pacific an IT component distribution Company and later Pineapplehead all Companies that have moved on or have been delisted. 

    Writing on his LinkedIn page Kourambas wrote “PIneapplehead, love it or hate it, it was a great name for a business. The truth is NOBODY forgot it. Pinepplehead was spawned out of a passion for Computers and most importantly Computer Graphics.

    in 1997 we successfully demonstrated Computer Animated graphics TV Golf broadcasting, this was a world first, later that year Pineapplehead was broadcasting live to air 3D simulations of golf greens for the Australian PGA tour with host broadcaster Channel 7.

    Pineapplehead went on to become the first company to use a MS Windows system, live to air on CBS USA.

    At one stage Lako Pacific was appointed the distribution partner of Pioneer along with several other leading brands.

    Today Kaiser Baas and Evan Kourambas are heading in a new direction developing “affordable technology” products for the mass market a move that big CE retailers around the world appear to like. 

    Their products which are about to appear in 400 UK stores across the UK are already proving popular in Australian stores with retailers such as Dick Smith, JB Hi Fi and Harvey Norman already stocking most of the Companies product range. 

    During a visit to the Companies Melbourne Offices ChannelNews was shown new drones and a Sedgeway type product that could be in stores by Christmas if liability issues relating to the $900 Sedgeway are ironed out.

    “Right now we are growing and growing fast, we are also operating across several geographies including Europe, Asia and the Middle East. We have just got back from a highly successful trip to Europe and the Middle East with several new big retailers set to take our products” said Kourambas.

    “During the past 12 months we have moved to refocus our product offering as some products have simply fall out of fashion due to several reasons. We have reinvented the Company and our product offering and we are now having a lot of success”. 

    “Our future is in adventure technology. This is a fast growing category being adopted by retailers who are looking for new categories as others slow down”.

    Shortly Kaiser Baas will launch several new products in Australia. 

    Cash Running Out At Dick Smith

    Dick Smith store management claim that consumers have deserted the retail chain and that on some day’s takings are a tenth of what the stores were doing during the same period last year.

    “We have no stock and the perception is that the house brand stock that we are being asked to sell at inflated prices are not worth the money” said a Sydney store manager.

    “We have had no real stock for months” they said. 

    ChannelNews has been told that the remaining management at Dick Smith, who are bleeding between $3M and $4M a week have been told that their jobs are secure until June the 30th with several managers locked into contracts until that date.

    One senior staff member said “I think this has been done so the receivers can try to strech out the life of the stores. The more it stays up the more they make in fees” they said cynically.  

    According to sources operating costs have blown out in recent weeks.


    Click to enlarge
    Neil Merola the former Marketing Directory responsible for the Companies marketing and selling operations is believed to have gone to ground after being sacked.


    As at December 2015 Dick Smith had $30M in operating cash, by January this was down to $20M, rent on current Dick Smith stores is around $2.5 to $3M a week. 

    Operating costs on top of the rent bill are believed to be a minimum of between $5.5 to $6.5M. Operating on 20 points margin they are generating revenues between $7M to $8M a week.

    There is also the possibility that receivers Ferrier Hodgson could start shutting down stores during the next few weeks as money starts to run out. 

    According to sources still working at the retailer these are stores that are were “unprofitable” prior to being placed into receivership as well as stores that prospective buyers have said that they have “no interest” in buying. 

    A prospective buyer told ChannelNews that they have concerns about the data provided via a “confidential data room” set up by Ferrier Hodgeson.

    The CEO of one prospective Company said “There are a number of issues relating to the possible acquisition of Dick Smith. Firstly, the brand is broken beyond repair, I believe consumers will struggle doing business in a Dick Smith branded store. If anyone does buy into what the receivers are offering they are going to have to do radical surgery, rebrand and that means a big investment in marketing”.

    “The other issue is that a lot of suppliers don’t want to see another competitor in the market up against Harvey Norman who are set to deliver excellent results similar to what JB Hi Fi delivered. Suppliers are looking to cut account management and merchandising costs and by concentrating on a select few retailers they believe that they can improve sales by working with a smaller group of retailers”.

    “We have walked away because there is too much risk, banks are not prepared to back risky ventures especially a retailer trying to take on Harvey Norman and Dick Smith”.

    The previous Dick Smith management team, headed up by former chief Nick Abboud had come to the conclusion in late November that they had to close stores if they were to cut costs.
     
    A plan submitted to their banks was rejected as there was “real risk associated with leases” said an insider. 

    According to Fairfax Media stock levels have been in decline since after Christmas 2014, according to staff, who claim a decline in deliveries of high profile brands coincided with an increase in home brand stock and accessories, despite feedback that the Dick Smith and MOVE product was not popular with shoppers.

    The demise of Dick Smith and the role of private equity group Anchorage Capital Partners in its transformation from a $94 million electronics business into a $520 million public company is now the subject of a Senate Inquiry, after independent South Australian senator Nick Xenophon gained bipartisan support for his inquiry into the collapse of Australian retailers last week.

    COMMENT: Why Is LG Letting B+O Climb On Their Back.

    At Mobile World Congress in Barcelona LG is set to reveal new top end smartphones that incorporate Bang + Olufsen BeoPlay audio technology, but one has to ask why the Company has done this?

    On one hand LG wants consumers top believe that their own Music Flow networked sound system is seriously up there. It delivers 24Bit sound playback capability.

    Several publications have described the LG sound offering as being up there with Sonos. 

    Digital Trends said of the LG Music Flow system “It offers competitive sound quality, with a dark and smooth punch of bass”. 

    So why not deliver the new LG G5 with Music Flow as opposed to Bang + Olufsen B+O BeoPlay technology which is only as good as the LG Music Flow offering but at twice the price.

    What LG is buying is a name, while Bang + Olufsen whose products are sold primarily via their own retail stores is getting global access to millions of consumers who will be exposed to the B+O branding on a smartphone and in global advertising run and paid for by LG. 

    Ironically, it is not true B+O Play technology that G5 owners will get. For example, the B&O BeoPlay A2 which is the smallest of the B+O BeoPlay products has two 3-inch, full-range drivers and two 0.75-inch tweeters on each side. They are reinforced by a couple of 3-inch bass radiators, which help to add depth and meaning to the lower frequencies.

    It’s a bit hard to get this technology into 5″ smartphone.
     
    The new B+O Play sound will be included in the LG G5, which is set to be announced this weekend in Barcelona.

    What an ‘enhanced high-quality audio experience’ means for users isn’t clear, from the announcement, but it looks like both LG and Bang & Olufsen are excited about the deal.

     
    Juno Cho, President and CEO of LG Electronics Mobile Communications Company said “By collaborating with B&O PLAY, we believe we can meet and surpass the heightened expectations of consumers seeking the next generation of enhanced audio experiences on their smartphones. Our collaboration with B&O PLAY has proven mutually beneficial and LG will continue to work with other leading companies in a variety of different industries in order to set new standards for innovation in the premium smartphone market”.

    So next time you are being told that LG’s Music Flow system is as good as a Sonos and it is, you may want to raise the question as to why LG is not using their own audio technology in their new Smartphones. 

    Ironically I don’t believe that too many people actually listen to music straight out of their smartphones. Most use headphones or connect the device to a networked system which defeats the concept of partnering with B+O.

    Maybe LG has a problem with their brand pulling capability. 

    Former Vodafone Executives Sues Optus For $14M Claiming Harrasment

    Senior executives in the marketing department of Optus which has been described as a “boys club” have been accused of “bullying, suppression and victimisation” by a former marketing executive who had only been in the job for 7 months.

    Now Kerry Morrison who has had numerous jobs prior to joining Optus wants the company to cough up $14.5 million in damages. She claims internal fighting between Optus’ marketing departments prevented her from doing her job.

    Morrison worked at Optus from 2002 to 2003, and returned to the company in 2011 where she worked as the Head of Sales, Services and Marketing in Digital Media for 7 months. Prior to that she also worked for 19 months at Vodafone Hutchison Australia.

    Optus said it would defend the ”unjustified claims”.

    Last year former David Jones PR executive Kristy Fraser-Kirk tried to claim $37 Million from David Jones for sexual harassment. The case which never went to trial ended up with David Jones paying out less $900K. 

    Last week Harmers Workplace Lawyers filed a statement of claim in the Federal Court in NSW claiming SingTel Optus failed to protect their client from a “known bully”.

    It was revealed that Optus terminated Morrison’s employment prior to the case being brought before the Federal Court.

    Morrison also claims that Optus tried to prevent her from exercising her workplace rights.

    Now Morrison is seeking millions claiming that her second spell at Optus had resulted in her reputation being damaged.

    One head hunter in the IT and telecommunication channel told ChannelNews, “I had never heard of this woman prior to this case, now everyone knows who she is because of her action against Optus”.

    Lawyers acting for Morrison have also sought orders “designed to ensure that the dysfunctional culture of bullying, suppression and victimisation at the Optus Group is reformed”.

     

    Fairfax Media claim that Morrison alleges Optus failed to warn her about tensions between the company’s digital media division, headed by Austin Bryan, and the consumer division, including “strong tensions in the relationship between Bryan and [head of consumer] Michael Smith; the protection of under-performing internal and external parties; [and] the role of Michael Smith as a known bully highly protective of the revenue of the consumer division”.

    Harmers allege Mr Smith would not support Ms Morrison’s initiatives to boost online sales “as it would lead to a decline of sales through retail”, which he managed.

    Tensions peaked at a meeting between the two on January 11 when Mr Smith allegedly “started screaming at [Ms Morrison] and became physically aggressive and overbearing to the point where [Ms Morrison] became fearful and emotionally upset [and was] physically shaking”, court documents allege.

    She claims she was sacked without notice by her supervisor, Mr Bryan, on February 7.

    Technology Driving Retail Sales 16% Growth Tipped

    Shares in Harvey Norman and JB Hi Fi rose yesterday after David Jones raised its full-year profit forecast to between 10 and 12%. However consumer electronics retailers could be facing major stock shortages due to global manufacturing cutbacks.

    Shares in Harvey Norman and JB Hi Fi rose yesterday after David Jones raised its full-year profit forecast to between 10 and 12%. However consumer electronics retailers could be facing major stock shortages due to global manufacturing cutbacks.

    During the past week several distributors have told ChannelNews that they are struggling to get stock after “massive global cutbacks” due to poor market conditions in the USA and Europe.

    Geoff Mathews the Marketing Director at Convoy the distributor of brands like Monster Cable Harmon Kardon and JBL said “Stock supply is very tight. While the Australian market has remained buoyant overseas markets have crashed and manufacturers have significantly cut back on the amount of stock they manufacture as a result priority is being given to markets other than Australia”.

    A Melbourne based supplier of sound and iPod attach gear to both Harvey Norman and JB hi Fi as well as the specialist channels said “The issue of supply is becoming serious. Vendors have cut back so far in some cases up to 50% on what they were producing in early 2008 that we are now struggling to get any stock”.

    “The Australian market is looking good however if stock is not going into stores it will hurt both retailers and distributors” they said.

    Earlier this year mass retailers lost millions in sales due to a lack of flat panel TV stock with several retailers only getting new stock in May. 
    In Australia the overall growth in the consumer electronics has been strong according to GFK who said that the market grew by 12% in the first quarter. Insiders are tipping 16% for the second quarter due to improved TV supply, notebooks and demand for digital camera in particular SLR camera’s that grew by 79% up to May 2009.

    “Mass retailer in the consumer electronics market, are expecting good growth this year due to tax cuts and Federal Government incentives and a rise in consumer sentiment” said a Merrill Lynch analyst.

     David Jones now expects full-year profit after tax to rise by 8-12 percent for the year to June, up from 0-5 percent forecast previously, due to a strong trading performance in May and June.

    “David Jones is a barometer for the discretionary spending sector so if David Jones is in itself experiencing some good conditions, I don’t see why the others won’t get some benefit too,” said Lucinda Chan, division director at Macquarie Equities.

    “The government stimulus is working quite well. People are going out and people are spending. Hopefully this would be a sign that markets are turning the corner,” Chan said.

    “Whilst we still have to trade through July to complete the fourth quarter and we are not planning to repeat the clearance of excess inventory undertaken in July 2008, our trading to date has been pleasing and well above our expectations,” David Jones Chief Executive Mark McInnes said.