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

    Smart Office

    Hello NFC: Vodafone Rev Up M-Payments

    Vodafone and Visa have announced the future.

    The future of mobile, that is.

    Near Field Communications (NFC), which basically allows you to pay for good with a swipe of your Android, will now be offered by a Vodafone SmartPass app, launching early 2013.

    It is the first telco to offer the service.

    You can get the smart, cash -free mobile payments via Vodafone’s new NFC-SIM and downloading the SmartPass app.

    However, it will require a NFC compatible smartphone to work properly – there are currently just three offered by Voda – including Samsung’s Galaxy SIII, HTC’s One X and Sony’s Xperia Sola, but sadly lacking on the iPhone 5.

    Samsung Galaxy SIII will come with Vodafone SmartPass app preloaded from next year.

    To activate, register for a ‘virtual’ Visa reloadable prepaid card using the Vodafone SmartPass app, and pre-load funds from your Visa or MasterCard payment card, and you can use your mobile to pay for anything from food to big tickets items, travel, loyalty or gift vouchers, by just waving it at the pay machine now found in most OZ retailers.

    The “game-changing” mobile application shrinks the contents of our pockets and makes for stress free shopping, the duo said today in a blog.

    “In the fashion of a Visa payWave card; Vodafone SmartPass relies on contactless, Near-Field-Communication technology to process transactions securely via the Visa network, direct from your smartphone, fast.”

    ANZ will be providing the virtual Visa prepaid card and it’s also the bank for loading funds but you dont have to be an ANZ customer; as long as your a Mastercard or Visa holder you can use the app.

    “NFC is fast-becoming the “it” feature for smartphones, and for very good reason,” and NFC payments are set to hit $110 billion in 2015, said Thomas Roets, Vodafone General Manager of Strategy and Business Development.

     

    “As early as next year, customers with NFC-enabled smartphones will be able to leave their wallets at home and pay for a morning coffee, an evening out with friends, or groceries, with their handset.”

    Vodafone says it will bolster its range of NFC smartphone  in 2013 and say 80% of smartphones it will offer on contracts will have NFC technology.

    “Vodafone SmartPass offers a simple, secure and convenient way for Australians to pay at hundreds of thousands of stores that already accept contactless payments,” said Visa Australia boss, Vipin Kalra.

    The Vodafone SmartPass solution has been built in close collaboration with Visa, Vodafone, ANZ and Rev and is part of the world’s largest mobile payments partnership. 

    Telstra Say Tata…& Make Global TelePresence Felt

    New Tata deal will provide Testra HD telepresence in 31 cities worldwide.

    Telstra’s new interconnection deal with Indian telco Tata Communications will allow its Cisco’s TelePresence customers connect to high def video conferencing equipment globally.

    Tata Communications’ Global Meeting Exchange provides private and public telepresence services spanning 31 cities across five continents. Telstra’s Cisco TelePresence managed service is delivered over its Next IP (TM) network.

    Telepresence allows virtual simulation of meeting rooms, joining participants who are at different locations and can assist with employee collaboration across remote locations, boosting productivity reducing travel time and costs.

    The comms tool “is an increasingly popular way to conduct global business meetings because of the time, cost and environmental efficiencies it delivers,”  saysPhilip Jones, Executive Director of Telstra Innovation, Products and Marketing. 
    The new agreement will help address the demand for telepresence meetings globally, he added. 

    To organise a telepresence meeting, Telstra customers will be provided with instructions on how to reach their conference on Tata Communications’ Global Meeting Exchange platform.  

    Assistance is available to Telstra customers via their local Telstra service desk number in each region.  

    The interconnection agreement will also benefit other organisations that do business across the globe, Peter Quinlan, VP, Integrated Business Video Services, Tata Communications, said.

     

    “The agreement with Telstra further demonstrates our commitment to drive Telepresence as a tool for true global collaboration where customers can connect with each other regardless of service provider or network,” Mr Quinlan said.

    Dick Smith Drags Woolies Down

    Woolworths feels the pinch as consumer electronics continues to slide.


    Click to enlarge

    Woolworth’s latest H2 2011 results, just announced, reveal net profit fall of 16.8% due to discontinued operations and $300 million provison it was forced to make for the divesting of Dick Smith, it said today.

    In January last, Woolies said it would sell off its Dick Smith electronics operation, and would close over 25% of its stores immediately here and New Zealand due to poor performance.

    As a result of the divestment, Consumer Electronics division has been disclosed as a ‘discontinued operation’, forcing it to make $300m provision and dragging earnings for H2 down for the Woolworth’s group.

    Dick Smith sales increased just 0.7% for the half year to December, a disappointing result in what should be one of its busiest trading period including Christmas, although comparable store sales did increase 2.4%.

    However, Woolies said sales for second quarter were “pleasing” given that consumer electronics continues to be impacted by the poor trading environment, price competition and price deflation in key products.

    The new format DS stores, which now account for 74% of all stores also continue to outperform older format stores and enjoyed sales growth of 8.7%, which could read as a sales pitch to any potential buyer.

    The process of getting rid of its electonics business is now underway and a “number of potential purchasers have expressed interest,” Woolworths Group said today.

    Read: Woolies Give Dick Smith The Boot

    Apart from electronics, Woolies’ other businesses are booming as total sales grew 5.2% to $ 28.9bn. Food and Liquor sales for the half-year were $19.6 billion – an increase of 4.3% on last year.

    Earnings before tax grew 4.1% from continuing operations (total Group EBITDA before Consumer Electronics $300m provision was up 3.9%).

    Net profits from Woolworths’ ‘continuing’ group operations including food, liquor and home improvement divisions which also grew to $1.1bn – up 3.1%, before consumer electronics was taken into account, the supermarket giant said.

    Consumer Electronics’ New Zealand sales increased 2.2% for the half year and comparable sales increased 6.5%, which it said was “strong” given New Zealand challenging macroeconomic environment and significant price deflation.

    Sales for BIG W, however, fell 1.3% for the half year to $2.4 billion. Comparable sales fell 2.8%, compared to a decrease of  4.2% in H1.

    Customer numbers and items sold increased during the second quarter (Oct-Dec period) although price deflation continued, averaging 5% and was most evident in Home Entertainment and Toys categories, the giant confirmed.

    “Trading over the Christmas was pleasing with positive customer and unit growth in December offset by deflation resulting in lower average basket sizes.” 

    Strong results were achieved in DVDs, Books, Toys, Sporting categories whilst cooler weather in December had a “modest” effect on apparel and outdoor categories.

     

    Earnings at the discount store fell 4.3% to $119.6m, however Woollies said the result for the second quarter was pleasing and showed positive growth, compared to 2010.

    “Woolworths Limited today reported an increase in net profit after tax from continuing operations of 3.2%. It was also pleasing to achieve an increase in our trading result of 5.6% before central overheads and our investment in Home Improvement,” said Woolworths Chief Executive Officer, Grant O’Brien.

    “This is a sound result considering subdued consumer confidence,deflationary pressures and the significant investment we are making in the business in line with ourstrategic priorities for growth.”

    Woolies also announced a 3.5% increase in fully franked interim dividend to 59 cents per share.

    Currently there are 386 Dick Smith stores between Australia and New Zealand. In January Woolworths will close up to 100 of these stores before the sale over two years.

    Gerry Harvey Gets Gloomy, Predicts Mass Closures

    Baton down the hatches: Harvey Norman boss is predicting dire times ahead for retail.


    As Harvey Norman owned Clive Peeters and Rick Hart outlets run aground, Gerry Harvey predicts similar fate for other electronics businesses. 

     “There’s no doubt in the world in the next six months many (electronic retailers) will have to close. It’s just a matter of how many,” he said in an interview this week. 
     Times are a-changing, the 71 year old admits. 
    “Every year in my life I’ve said this is going to be the best one ever”, confessing he no longer holds this sunny outlook, predicting a dismal sales period in H2 for Aussie retail. 
     However, there was some light at the end of the tunnel – called iPad 2, which was “selling like crazy” in store, he said, with Android’s playing catch up. 
    But laptops and flat screens were delivering less joy to the bottom line – while volumes are up, profit margins and revenue are tightening.  
    However, the retailer did recently report growth in white goods, cooking, home appliances and floor care. 
    He also said competition from online traders was “negligible”, which is interesting, considering only yesterday the Harvey Norman Chairman confirmed his giant was entering online world of selling next month. 
    “Eighty percent of our retail products will be online in September,” he confirmed yesterday. 
    “I’m sick of talking about online [retail] to journalists” a fed up Harvey added. 
     

    These latest comments suggest Harvey is still in denial about the elephant in the room that is online trading (or lack of) and the huge impact it has had on his bricks and mortar business. 

    Gerry Harvey’s previous refusal to sell goods on its website may be one of the reasons behind its dull sales figures – posting a 3.6% fall in its most recent results last week. 
    Global sales for the year to June 30th were $6.18 billion, with the retailer saying revenue had fallen in its franchisees due to a challenging operating environment, a stronger Australian dollar and falling TV prices. 
    And this is part of an ongoing southward profit trend at the retailer.  
    In its previous results to December 31 profits slid 16.5% to $198.61m (before tax) – from high of $237.77m, in spite of having increased its store numbers from 195 to 198. 
    Harvey’s have now moved to close seven Clive Peeters and Rick Hart group stores due to poor sales following its acquisition last year for $55M, announced last week. 
     In comparison, Harvey’s biggest rival and darlings of the web, JB Hi Fi, announced a 13.3% rise in profits and an 8.3% rise in sales.  
    And ditto for Dick Smith, who also have a massive online presence, reporting a 7.1% jump in comparable sales for the full year to June 26, which it attributed to “the refreshed online store.”

     

    Doesn’t that tell you something, Gerry? 

    Len Wallis On Sound, Big TVs And Why Foxtel Better Watch Out

    Len Wallis a leader in the specialist AV market is restructuring in an effort to better engage with customers.

    The Sydney based AV specialist is undertaking a major refit to better engage with his customers who spend up to a million dollars on AV and automation fitouts. 

    Len Wallis’ showroom at Sydney’s Lane Cove already has 10 sound-lounge/theatres, a virtual smart-house and an on-site service centre.

    But in a time when bricks-and-mortar stores is in decline and facing economic uncertainty, why is Wallis developing his state of the art showroom even further?

    The “substantial drop in price points of AV equipment” is driving traffic into his store, owner Len Wallis admits, but is also the result of “genuine increased interest in better quality by the consumer,” he says.


    Click to enlarge

    Demonstrating the gear in the connected environment is also critical, says Wallis, as is a WiFi network.

    Having an in-house wireless network is “absolute and we developed our system over time,” he says but “we’re pulling it out and have just installed a really stable network.”

    “Its got to be done” he admits.

    So what are the big in the AV business at the moment?

    Security is a big issue for custom gear but from Len Wallis’ point of view, entertainment is the main one.

    “The changes in Australia over six months are going to change everything as far as content provision goes ..the days of listening to music the way we used to are totally dead now. 

    “Entertainment is now about the sheer amount of content available and the ease of getting it as you can stream content straight to your system rather than messing around with iPods and downloads.”

    This means content players of old may be in for a shock.

    Traditional content providers including the likes of Pay TV giant Foxtel “is going to have to be very careful that a video version of Mogg suddenly appears on their doorstep and then they will have real problems.”

    There are a few players working on delivery at the moment so, watch this space, he says.

    The likes of Google, Apple and Microsoft are “perfectly positioned” to enter this market and Wallis reckons it’s where content is going to go.

    “Foxtel is going to have to pick a side and partner up with someone,” if they are going to make it in the new content provision era of cloud, streaming and IPTV.

    Quality of devices is also improving in general, with most now starting at 320Kbps instead of 128Kbps and there’s now an upgrade path available on audio files, but the sheer amount of content available is the big thing.

    Quality of projectors has also jumped and customer feedback has been extremely positive, Wallis adds.

    In the custom space, the AV guru says there is still traction in non-dedicated theatre systems which is going away from the general trend.

     

    Sonos is still “enormous..it’s everywhere and people are still going to multiple boxes as its simply, cheaper and easier to handle.”

    The issue of service provision is also on the horizon for Wallis but there’s an interesting twist on it.

    “Some customers are looking to us as service companies to install and program devices they bought at somewhere cheaper like Bing Lee.

    “They’re looking at us to do the service on it as they perceive someone else as cheaper.

    “However, we’re not geared up as a service company of that nature just yet..but it’s certainly something we are going to look at”, he says.

    So what about the TV market?


    Click to enlarge

    Wallis cites Panasonic VT series 65″ Plasma TV which he says is “unbelievable..a stunning screen” models but availability is scant on the Australian market.

    However, you “cannot get a Panasonic screen for love nor money in Oz at the moment,” he says.

    This supply side issue is due to Pana’s local entity not ordering enough, despite the company saying otherwise.

    “I would have sold 30-40 on orders [if the stock was there], which for us is a lot as we’re not a screen company. “

     And is the demand for larger screens like 75″ or 80″ growing as prices fall and bigger OLED screens emerge?

    “We don’t stock anything above 65″ in the shop, but we’re certainly looking at it,” Wallis says.

    “We’re being very cautions of who were dealing with at the moment,” he tells ChannelNews.

    “We’re selling screens but not making any money out of them in some cases,” resounding the battle cry on every retailers lips at present.

    The old reliables of pricing and margin squeeze are still a big problem, and customers are still not sure about spending which is a “big issue,” Wallis says.

    “But those customers who are spending are bringing their expectations down,” he notes. 

    Mouthy Kyle “Deeply Offensive & Derogatory”: Watchdog

    Hey Kyle, your comments were “deeply derogatory and offensive”AND you’re in breach of the Radio codes of practice.


    Click to enlarge

    The Communications watchdog has given 2Day FM’s Kyle Sandilands a taste of his own medicine, branding his comments on the Kyle & Jackie O Show about News Ltd Journalist on 22nd November as offensive and amounted to a breach of the Commercial Radio Codes of Practice 2011.

    Motormouth Sandilands branded News.com.au deputy editor Alison Stephenson a “little troll,” “a bullshit artist,” criticising her hair as “very 90s” and her blouse on his show last year. His remarks prompted outrage and led to advertisers including Vodafone, Telstra and the Good Guys dropping their sponsorship of Kyle and Jackie O show.

    “You haven’t got that much titty to be having that low-cut a blouse. Watch your mouth or I’ll hunt you down,” he said.

    A “fat slag” was also another remark that came from the smarmy Radio DJ’s mouth.

    And it looks like Sandilands’ comments has landed his employer 2Day Fm in a whole lot of trouble with the Australian Communications and Media Authority.

    “The Authority found the comments by Mr Sandilands deeply derogatory and offensive and, in all the circumstances, a licence condition is the appropriate response,” said ACMA Chairman, Chris Chapman.

    ACMA has now begun “formal steps” to impose a second licence condition on 2DAY FM which would “prohibit it from broadcasting indecent content and content that demeans women or girls,” it said today.

    If 2DAY-FM does not comply with its additional licence condition, ACMA may suspend or cancel the licence, it warned today.

    In response to Kyle’s rant , 2DAY-FM introduced several safeguards, including: instructing Mr Sandilands and his management of the sort of remarks that are unacceptable and must not be repeated and extending the broadcast delay for the program from 10 seconds to 30 seconds

    2Day FM also said it will be Installing a red light warning system in the Sydney (and Los Angeles) studio to notify announcers when content may be of concern.

    However, this is not enough said ACMA. In 2010, the same program breached the equivalent indecency provision of the earlier version of the codes.

    However, Austereo rejected ACMA decisions, saying ”2Day FM’s core audience is women, predominantly young women,” Southern Cross CEO Rhys Holleran said.

    Holleran also branded the watchdog’s license conditions as unworkable, saying “ACMA has issued no guidance on the licence condition and in light of that, we consider the condition to be unworkable.”

    ”Our difficulty with the proposed licence condition is that terms such as ‘decency’, ‘demeaning’ and ‘undue emphasis on gender’ are broad and ambiguous and mean different things to different people.”

     

    The ACMA appears to have ignored the steps which 2Day FM put in place, he added

    “In a 10 year period in which 2Day FM has broadcast approximately 87,000 hours of programming, it has breached the Code only four times.”

    2Day FM will have the opportunity to make representations to the ACMA on the proposed licence condition before a final decision is made.

    Fetchtv Flogs Cantonese Packs $49

    Fetch are reaching out to 244,000 Cantonese speakers in Australia with launch of new TVB pack.


    Click to enlarge

    244,000 Cantonese speakers in Oz are now being offered five of the “most popular” channels from TVB, one of Hong Kong’s largest broadcasters, including TVB Jade, Junior, Xing He and Lifestyle.

    Fetch’s IPTV Cantonese pack costs $49.95 per month, plus basic subscription which starts at $9.95 and the five channels available include:

    TVB Jade (TVBJ), boasts Hong Kong latest drama series, news and information from mainland China and Hong Kong and access to Australian shows including Australian News, Public Forum, Today Tonight, Finance Caf_ and Smart Guide.

    TVBN is another current affairs station in the pack showing 24/7 news and current affairs from Hong Kong and around the globe.

    TVB Junior for young viewers and parents, screening Cantonese language learning programs, general knowledge, cartoon, and dramas series, while Xing He – the world’s first Chinese TV drama channel showing TVB’s classic drama series.

    TVB Lifestyle screens shows hosted by celebs and other professionals on health, travel, food and fashion trends in China and abroad.

    Because FetchTV is delivered over broadband from ISP’s Optus, Internode, iiNet, Adam Internet, Westnet, Netspace and MyTelecom, the new service can be accessed by those in apartments and rented accommodation.

    Programming is also unmetered, meaning it doesn’t count towards internet downloading quotas.

    The FetchTV IPTV service includes: a 1 terabyte personal video recorder to record live TV, access to on-demand new-release movies, and other content from providers.

    Read: Haa! Shi! Optus FetchTV Invades Asia Here

    Last month, Optus announced expansion of its fetchTV IPTV service, MeTV, introducing 51 new foreign language channels and 4 world packages – offering Hindi,  Chinese Mandarin, Korean, Mandarin (Singaporean / Taiwanese) programs.

    iiNet also has several Chinese and Mandarin world packages for Asian audiences as well as others from India, South Korea and Hong Kong.

    The Cantonese channels are live on all ISP services, except Optus, which will commence later this month, Fetchtv confirmed.

    “The TVB Cantonese pack offers the very best of programming for Australia’s large Cantonese speaking population. The TVB channels are extremely popular in Australia, but this is the first time they have been available without a satellite, making it an ideal choice for customers living in multi-dwelling units, or for those customers looking to combine the very best of Cantonese and English programming,” said Scott Lorson,  FetchTV CEO.

     

    “TVB is pleased to partner with FetchTV in making our Cantonese channels available to those Australian households who are not currently able to access them,” said Patrick Wong of TVB.

    Together, TVB & FetchTV will now be able to connect even more Australian Cantonese speakers with home. ”

    Huawei Ascends $499 Smartie To OZ

    Look out iPhone: Chinese giant Huawei is planning to ‘disrupt’ the market with its dual-core smartphone for $499.
    The Ascend P1, which has been flagged as “where beauty meets brains,” is a slim smartphone with a 7.69mm body but houses a super bright 4.3″ qHD Super AMOLED display and potent dual-core 1.5GHz Cortex-A9 processor for faster multi tasking.

    Similar to the S III, Google Nexus and other Androids like HTC and Motorola, Huawei’s “flagship” Ascend P1 runs Android’s latest platform, Ice Cream Sandwich.

    Other specs of note include an 8MP high dynamic range camera with flash, Full HD 1080p video recording, Dolby Mobile 3.0 Plus for impressive 5.1 surround sound when connected to a home theatre system and 1GB RAM.


    Click to enlarge

    The dual flash LED and HDR effect optimisation on the camera will “capture every detail in any environment,” says Huawei who until recently stuck to low end budget devices, but have now upped the ante and could prove a serious rival to other Android players in OZ.

    Software wise, Ascend P1 also allows you edit PowerPoint and PDF documents.


    Click to enlarge

    “The Ascend P1 is not only a highly capable smartphone packing powerful technology; it offers users something that is beautifully designed and crafted,” commented Mark Treadwell, Head of Marketing, Huawei.

     

    “The launch of this flagship mobile in Australia is further proof Huawei is a disruptive player in the competitive smartphone industry. We are committed to providing Australian consumers with high-quality mobiles combined with honest pricing

    The Huawei Ascend P1 can be purchased outright from Dick Smith for $499, but no other carriers are selling the device to our knowledge, although Mobicity are selling it for $489.

    The Chinese brand are set to launch a slew of high end mobile devices in Oz later this year. 

    Priced This Way: Amazon Flogs GaGa At $0.99, iTunes $11.99

    Death to iTunes? A massive music battle is underway – and its not between the artists.


    Click to enlarge

    Amazon has come out with an all guns blazing, death to iTunes strategy, pricing Lady GaGa’s new album at its digital download music market place at a measly $0.99 on Monday last, the first day of sale.

    The same release was priced at $11.99 on its rival iTunes, who despite the cut ptice bi its music rival also reported brisk trading on what is tipped as one of the biggest releases of the year by possibly one of the most controversial sensations of the 21st century.

    One music industry sources indicated first day sales fell in the 250,000 – 350,000 bracket between the two download retailers. 

    E-tailing powerhouse Amazon is also pricing the album at a massively competitive $6.99 thereafter – well below the wholesale price of around $9, the WSJ believes.

    It is also selling many other titles by Katy Perry and Arcade Fire at $5 a pop.

    However, although iTunes accounts for 70 per cent of music downloads in the States alone, it must also be remembered mass retailer Amazon.com is one of the most visited sites worldwide including Australia, thus can attract eyeballs to cause Apple some headaches.

     

    But it wasn’t just the latter that Amazon’s Lady GaGa loss leader strategy has caused issues for.

    Its own servers crashed as a result of the cut priced album sending fans in to a frenzy to snap up the deal.

    “We have been experiencing high volume and downloads of today’s Deal of the Day, ‘Lady Gaga, Born This Way,’ have been delayed,” the company announced in a statement on Monday.

     “All customers who order this album today will get the full  album for $0.99.”

     Amazon introduced its cloud player service for Android OS earlier this year, although the service is not yet available in Australia.

    Its Coming: .XXX Porn Names Open, Thousands Celebs Banned

    Angelina Jolie, Beyonce and even former British PM Margaret Thatcher are all banned as .XXX domain names for porn sites, which has officially opened for registrations.

    The .XXX domain is a sTLD (sponsored Top Level Domain) designed specifically for the ‘adult entertainment’ and porn industry and will separate the dodgy sites from the more legit ones. 

    And 4,000 celebrities have already been banned- from Donald Trump to Angelina Jolie and Beyonce. 

    .XXX applications, opened by ICM Registry today, with the ‘Sunrise’ registration program, the first part of the three-part rollout of “red light” domain, which is set to go live on December 6. 
    1,500 .xxx domain names have already been allocated with 35 porn companies, according to reports. 
    Sunrise A allows registered trademark owners and existing registered domain names to apply for .XXX domain names. 
    If you own an existing domain name that isn’t trademarked, like adultdomain.com, you can apply for the matching name. 
    Registration, being taken by Florida based  ICM Registry, means the adult sites will be a “trusted brand, globally recognized and extolling responsible and safe behavior.” 
    Holders of .XXX domains will also benefit from “global marketing campaigns and greater awareness” in the mainstream world, say ICM, who is also developing a search portal for .XXX sites, like a Google for the sex industry. 
    And for those businesses who wish to stay out of the porn domain – can register their names to be blocked. 
    So, we’re sure Apple.XXX or Acer.XXX wont be appearing on the web any time soon. 
    Registration for this first phase runs for 52 days, starting from September 7. 
     

    For more information,  visit ICM Registry