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; } } Web Development TBOadmin123, Author at Smart Office - Page 3 of 7

    Smart Office

    JB Hi-Fi Reports 33 Percent Growth

    As pessimists talk down the CE market, JB Hi-Fi has reported 32.5 percent growth and $25.81 million in net profits.

    The result will put pressure on Harvey Norman as JB Hi-Fi over the next quarter will move into computing as well expand into new stores in both Australia and New Zealand.

    As Harvey Norman franchisees squabble over CE sales between computing and consumer electronics, JB Hi-Fi is executing a rapid expansion program.

    JB Hi-Fi opened 18 new stores during the year, bringing its total number to 66, and expects to open another 13 this financial year, including eight in time for the Christmas rush.

    The $25.81 million profit comes as a significant increase to its $19.5 million profit a year ago. Sales for the last fiscal year rose 36 percent to $945.8 million, up from $693.9 million the year before.

    Following the announcement JB-Hi-Fi shares closed at A$4.79. They are currently sitting on A$4.68. The company declared a fully franked final dividend of four cents per share, up 11 per cent.

    Excuse Me Can I Have My Identity Back Please

    The chance of your identiity being nicked is high according to a one time rogue whose claim to fame became a Hollywood movie. So what can you do about it.

    Nobody knows the ins and outs of identity fraud better than Frank Abagnale. Before becoming a financial security entrepreneur, he gained renown as the young man whose brazen impersonations enabled him to swindle a couple million bucks–and whose story was so colorful it was made into the movie Catch Me If You Can.

    Abagnale, who concocted his false identities rather than stealing them, is now turning identity theft into something of a personal crusade. “I know cons,” he writes in his recent book, Stealing Your Life: The Ultimate Identity Theft Prevention Plan, “and right away I saw that this one was going to be the sweetest of all.”

    Times have changed in the 40 years since Abagnale’s heyday. Identity theft, broadly defined as everything from purloining a credit card number to misusing bank or other accounts to full-blown impersonation, is commonplace now. A survey found last year that close to 3 percent of all households were victimised during a single six-month period of 2004. To put this in perspective, only 3 percent of households were burglarized during all of 2005. Yet most people take more steps to keep burglars out than to thwart financial crooks.

    The real issue is data security, at every level. Anyone can be at risk for snooping, data misappropriation, or identity theft, but successful entrepreneurs have more to protect than most people. The US Justice Department survey found that households with incomes exceeding $75,000 were among those most likely to fall victim. The biggest cost to entrepreneurs may be in the time required to sort out a nefarious impersonation. The Federal Trade Commission has estimated that in 2002 identity theft victims spent between 15 and 60 hours untangling the mess.

    Abagnale’s book offers many suggestions–scrutinize your monthly statements, don’t disclose private information, use strong passwords. Much is beyond your control, Abagnale notes; for example, key private information for thousands of individuals is often lost or stolen on laptops or to hackers. But there are things you can do–including protecting your own laptop. If your laptop goes missing, crucial business as well as personal data could be compromised. The first line of defense is to keep track of your laptop and when possible, lock it with a cable. You should also use a strong start-up, or BIOS, password (see your owner’s manual), and make sure your notebook can’t be booted from a floppy or CD drive. You can also use data encryption, which is built into Windows XP Pro or the new Windows Vista.

    Several companies offer software that they say will locate a missing notebook computer via the Internet. John Livingston, CEO of Absolute Software, says his company already has agreements for its products to be preinstalled in many laptops by Lenovo, Dell, Panasonic, and Fujitsu. The software clocks in with the company daily; once you report your notebook stolen, the company checks on its whereabouts and works with local law enforcement to get it back. Absolute claims a recovery rate exceeding 80 percent.

    But the biggest weapon in your arsenal against identity theft is cryogenic, and you should use it–by freezing your credit. If you’re a successful entrepreneur you’ve probably got more credit cards than you need.

    One more tip: Use credit cards–your liability is strictly limited, and theft of a credit card number is among the easiest types of identity theft to resolve. Abagnale recommends against paying for anything by check–especially if a merchant wants your driver’s license number or other proof of identity. A check, after all, shows your address, your account number, and your signature for all the world to see–and steal.

     

    King Street Wharf Takes Off As Macquarie Bank Moves In

    King Street Wharf is set to become one of the most sought after of CBD office locations with big names like Macquarie Bank, American Express, KPMG and now Macquarie Bank moving in.

    The new Maquare Bank building takes up an entire city block. wrapped in metal the two buildings which are side by side consist of 11 floors with a park between each building.

    Already property prices in the area have risen significantly with a 184 square metre top floor property at 15 Lime Street selling last year for $2,600,000 to Mediterranean Shipping.  The property had 166 metres of internal space. A mecca for small medium business King Street Wharf is primarily owned by either private investors or owner occupiers.

    Click to enlarge

    According to Multiplex King Street Wharf comprises of five stages with an overall forecast completion value of approx $1 billion. Covering approx five hectares, the total development includes commercial, residential, and retail, entertainment, serviced apartments and a hotel. It also involves associated infrastructure such as roadwork’s, car parking, charter vessel berthing facilities and coach parking.

     

    Greg Collins of Vertical Property Group said, “Demand in the area is at a premium. We currently concluded a 3 year lease at $680 a square meter with a 5% annual rise. With Multiplex taking up residence and American Express set to move in this year the precinct is fast becoming one of the most sought after CBD business addresses”.

    Click to enlarge

     
    By 2009 the King Street Wharf precinct is set to have over 20,000 people working in the area. The area is also set to be the gateway to the East Darling harbor precinct which will take up more than 22 hectares of waterfront running from King Street Wharf.
    Late last year NSW Premier Morris Iemma announced the winning entry in a competition to design a new recreational, commercial and residential precinct on a 22-hectare site at Sydney’s East Darling Harbour.
    Mr Iemma today said an 11-hectare waterfront park was the focal point of the winning design by Sydney design team Phillip Thallis, Paul Berkemeier and Jane Irwin.
    The redesign of the current container terminal also would include a waterfront promenade completing the link between Woolloomooloo and the Anzac Bridge.
    A public competition will be launched to find a name for the site, which is to include commercial and residential buildings.
    “This is an exciting new vision for Australia’s global city which will restore public access to this part of the foreshore for the first time in a century,” Mr Iemma said in a statement.
    “The redevelopment will also include enhanced port and passenger terminal facilities to accommodate growth in international passenger shipping.” 
    East Darling Harbour

    Construction is expected on the first stages of the project within two years.

    For information on King Street Wharf property go to:

    http://www.verticalpropertygroup.com/

     

    Wesfarmers Gets Coles

    Wesfarmers which has already started selling home automation gear via its Bunnings Hardware stores is set to take over Coles, the owner of Harris Technology, OfficeWorks, K Mart and Target.

    Wesfarmers Ltd, Australia’s biggest home-improvement chain, agreed to buy Coles Group Ltd. for $20.7 billion to become the nation’s largest retailer.

    Coles shareholders will get A$17.25 in cash and stock, 7 percent more than the latest closing price, the Melbourne-based company said in a statement today. The takeover is a record for Australia.

    Wesfarmers was the only bidder for the company’s 3,000 supermarkets, discount stores and office supply outlets after buyout firms TPG Inc. and Kohlberg Kravis Roberts & Co. dropped out of an auction. Chief Executive Officer Richard Goyder plans to revive sales at Coles, which are growing at the slowest pace on record, by winning back customers from rival Woolworths Ltd.

    “Over the last six months Coles management has been more focused on doing a deal than running the business,” said Steven Robinson, who helps manage A$550 million at Alleron Investment Management in Sydney. “You saw in the last quarterly numbers that sales weren’t as strong as expected.”

    Wesfarmers owns 12 percent of Coles after buying shares in April at A$16.47 each, including a 5.8 percent stake held by former Chairman Solomon Lew. Under that deal, Wesfarmers agreed to pay Lew extra if it increased the offer to other shareholders.

    Coles shares closed June 29 at A$16.12, and were suspended today. They have fallen 10 percent from a record A$17.95 on May 14 as the prospects of a bidding war diminished.

    Coles Vindicated

    Investors will receive A$4 cash and 0.2843 Wesfarmers shares for each Coles share they own. They will also be entitled to a 25 cent dividend, which will be paid by Coles before investors vote on the bid in October.

    Coles Chairman Rick Allert said today’s deal vindicated the board’s decision to reject a A$15.25 a share offer from buyout firms led by KKR in October. Coles shares were trading at A$11.70 Aug. 16, the day before the board first revealed the KKR approach.

    Wesfarmers shares closed Friday at a record A$45.73, valuing the Perth-based company at A$17.7 billion.

    Including debt, the offer values Coles at A$22 billion. That tops the A$16.6 billion Mexico’s Cemex SA agreed to pay for Rinker Group Ltd. in April, at the time the biggest takeover of an Australian company.

    The deal takes the value of announced takeovers in Australia this year to $113.8 billion, compared with $70.3 billion at the same time last year, according to data compiled by Bloomberg.

    Solo Bid

    Goyder decided to proceed with a bid after his buyout partners, Permira Holdings Ltd. and Pacific Equity Partners, withdrew just before the June 30 deadline for bids because of rising borrowing costs.

    Under his original proposal, Goyder would have taken control of discount department store Target and Officeworks and split ownership of the supermarkets, Kmart, liquor and fuel outlets with the buyout firms.

    Wesfarmers will now retain all the Coles assets, enabling Goyder to overtake Sydney-based Woolworths as the nation’s biggest shopkeeper. Wesfarmers owns the Bunnings chain of do-it-yourself stores.

    “It’s a risky acquisition, there’s no doubt about that, but it’s potentially a company maker as well,” said David Spry, an analyst at FW Holst & Co. in Melbourne.

    Deutsche Bank AG and Melbourne-based Carnegie, Wylie & Co. advised Coles. Wesfarmers is being advised by Gresham Partners and Macquarie Bank.

    For Sale

    Wesfarmers was created as a farmer-owned co-operative in 1914, the same year Coles opened its first supermarket in the Melbourne suburb of Collingwood. Goyder, 47, became just the seventh man to run Wesfarmers when he was promoted to CEO two years ago.

    Coles put itself up for sale in February after the failure of CEO John Fletcher’s A$910 million program to increase sales by rebranding most Bi-Lo discount supermarkets and Kmart stores under the Coles name and opening so-called “supercenters” combining groceries with discount department stores.

    Coles operates Kmart under license from Hoffman Estates, Illinois-based Sears Holdings Corp.

    Goyder said he will focus on improving customer service to win back customers.

    “How you improve any retail business is improve the product offer to customers,” Goyder said on a conference call. “This isn’t an overnight turnaround, this is something that will take a lot of time.”

    Credit Defaults

    Coles’ sales rose 0.6 percent in the 13 weeks ended April 13, the slowest quarterly growth on record. Woolworths sales in the period climbed 8.8 percent.

    Wesfarmers will issue as much as A$14 billion of stock and take on A$8.1 billion of new debt to fund the deal.

    The perceived risk of owning Coles bonds fell after the deal was announced. The retailer’s BBB credit rating, the second-lowest investment grade, was placed on review for a possible upgrade by Standard & Poor’s.

    Wesfarmer’s A- rating is on review for a possible cut at S&P.

     

    Telstra Rolls Out SMB Retail Stores

    Telstra has launched the first of a national network of retail outlets designed to service small to medium sized businesses.

    The Business Centre offers three demonstration areas for home, office and mobile businesses and is staffed with specially trained small business consultants for customers to arrange one-on-one meetings, Telstra says.

    Telstra Business Group Managing Director Deena Shiff, said the Business Centre was part of an overall strategy to align Telstra Business with its customers.

    “We are very committed to supporting business solutions where small business operates,” she said.

    The first store is located in Parramatta, in Sydney’s west, which Telstra says is home to more than 240,000 businesses and is Australia’s third largest regional economy.

    Telstra Business is also offering its customers the chance of winning a Next IP technology “makeover”, posting the developments of the SMB’s technology transformation on a blog hosted on the Telstra Business homepage.

    The selected small business will receive 12 months of service for free. See www.telstra.com/business to apply.

    Use Google Search On Your Own Site

    Google has launched a low-cost application enabling small businesses to utilise its search technology on their own websites.Last week, the search-engine giant launched a Custom Search Business Edition – a product that allows businesses to put a Google search box on their websites and enhance results for visitors searching within.

    “Millions of businesses have a Web presence but offer users no ability to search their site,” Dave Girouard, vice president and general manager of Google Enterprise, said. According to Girouard, small businesses typically spend money on search engine optimisation to bring customers to their site, but they often don’t invest in making the site easy to navigate.

    The Custom Search Business Edition does not require any software or hardware installations for set up. Once users purchase the product, they will go through a short setup wizard and will be directed to copy and paste a line of code into their website. Users will then be able to customise the search page with their own logo and colour scheme.

    Additionally, small businesses have access to a reporting tool that delivers information about visitor behaviors, including reports of daily and monthly use of the search engine as well as popular search terms. Users can then customise search results with the Extensible Markup Language (XML) feature, which can be used to guide customers to specific areas of the website.

    Custom Search Business Edition costs US$100 a year for searching up to 5,000 pages, and then jumps in price to US$500 a year for up to 50,000 Web pages.

    For more information, visit www.google.com/enterprise/csbe

    Dangerous Toys Recalled

    Massive product recalls of unsafe toys from leading US manufacturer Mattel has prompted the ACCC to warn local retailers of the need to step up quality testing.Almost half a million toys sold in Australia by  Mattel are being recalled because of safety concerns, including 23,000 toy cars and more than 400,000 toys containing magnets.

    The affected toys included Batman, Barbie and Cars products.

    Australian Competition and Consumer Commission (ACCC) spokeswoman Louise Sylvan says the states carry out random testing on products to ensure there are no problems.

    But she says retailers have the major responsibility to ensure what they sell is safe.

    “Where they are possibly using suppliers anywhere in the world who are distant, who they may not be absolutely knowledgeable about, they need to step up the quality assurance programs [and] the rate of testing of their products so that these problems can be stopped before they actually land on the shelves,” she said.

    CE Market Has Plunged Claims GfK

    The sales value of the CE market in Australia has plunged according to research group GfK. However it does claim that the next few periods will be steady. Holing up the market is flat panel TV sales which account for nearly half of all CE sales.

    According to GfK Australians are on track to spend a record $5 billion dollars on digital devices by the end of 2007. Some $2.1 billion will be spent between January and June suggesting a big finish to the year, especially over the Christmas period.

    However GfK warns all is not completely rosy in the consumer electronics world. Sales growth has plunge a way below the rate recorded in previous reporting periods.

    Total sales in H1 2007 were up only $200 million, or 10 percent, on the same period of last year – in contrast to 43pc growth in H1 2005 and 41.7pc in H1 2005.

    And sales value actually dropped by around $300 million, or 12 percent, compared to the second half of 2006, largely due to continuing price drops.

     

    Product categories showing growth in the latest report include media players (eg, iPods), game consoles, digital still cameras and multi-functional devices. But sales were down for DVD players, video cameras, inkjet printers and photo printers.

    “The majority of Australian households now own a digital still camera and DVD player, causing category growth to slow as these products are now mainly purchased as upgrades and not as the first household purchase,” commented GfK’s David Griffin, who authored the report.


    In the “image and print” sector, inkjet printer sales were down 42 percent, photo printers dropped 35.6 percent and digital camcorders were down 3.3 percent. But multi-function devices were up 16.6 percent. Among Companies likely to be hit by the slow down in inkjet printer and digital camera’s is Canon who operate across both markets. While Canon is reporting increased sales they refuse to reveal profitabilty for the two markets.

     

    Flat panel TVs are where the money is, accounting for more than half (52 percent) of all spending on the index product categories. LCD TVs have overtaken plasmas, making up a third of sales value (32 percent). GfK attributes this dominance in part to the wider range of screen sizes available for LCD TVs.

    Other big sellers were digital still cameras ($293.7 million); media players ($175 million); games consoles ($149.2 million), camcorders ($107 million) and DVD players ($107 million).

    The index is compiled by GfK using information provided by GfK’s retail audit panel.

    Stenmark Loses Sony Ericsson PR

    Public relations company Stenmark Organisation, which recently lost the Sony Australia account to Hausmann Communications, has now also lost the Sony Ericsson account to Hill & Knowlton Australia.


    Stenmark and Sony Australia ended their eight year PR association in June, though at the time said the Sony Ericsson account would remain with Stenmark for the time being.

    In a joint statement issued by Hill & Knowlton and Sony Ericsson, the companies said the account is transitioning from Stenmark, which is winding down Sony Ericsson activity by mid-October.
     
    Hill & Knowlton will take over into the pre-Christmas period launch of new Sony Ericsson phones and accessories.

    Over at Hausmann, Account Manager Jacqui Christie who handled Sony Australia PR, is leaving the company as of today, to move to London. Her replacement is Alexis Bicknell.

     

    Sky Launches 24-Hour Business Channel

    Sky News will air Australia’s first 24 hour, seven days a week dedicated business channel on Foxtel in early 2008.

    The Business Channel will deliver breaking business news, commentary and analysis around the clock from Australia and internationally. It will be produced from a new dedicated business news studio at the Sky News digital centre in Sydney. It will also have a bureau in the Sydney CBD and in Melbourne.

    Foxtel promises a minimum of 16 hours a day of Australian-made programming on the channel. It will also draw from the Fox Business Network in the US, BSkyB in the UK and Reuters Televsion, as well as locally, from The Australian newspaper.

    It will also feature a live on-screen ASX ticker and plans are underway to add an interactive multi-screen business application similar to that already offered by Sky News.

    Foxtel CEO, Kim Williams AM, said: “Foxtel is very excited about delivering this new service to our subscribers, who now have access to eight dedicated news channels including three dedicated business channels. Sky News Business Channel’s emphasis will be on providing quality news and information that really matters to Australians.”

    Sky News Business programs are available online and can be downloaded to audio iPod via the Sky News website – www.skynews.com.au
     
    Sky News Business programming is also available on the Hutchison, Telstra and Vodafone 3G Networks.

    ANC, owner of Sky News is also planning for Sky News’ expansion into mobile television services. Sky News has been participating in the Digital Video Broadcasting-Handheld (DVB-H) mobile television trials in Australia. SKY NEWS will offer video podcasting services to complement its existing audio podcasting services.

    The company says “The future is about personalising news services for individual consumers 24 hours a day – and Sky News aims to be the leader.”

    Details of the launch date for the Business Channel will be revealed soon.