public String DSASignature;
+ public String Summary;
+
#region IComparable<NetSparkleAppCastItem> Members
public int CompareTo(NetSparkleAppCastItem other)
private String _castUrl;
private const String itemNode = "item";
+ private const String descriptionNode = "description";
private const String enclosureNode = "enclosure";
private const String releaseNotesLinkNode = "sparkle:releaseNotesLink";
private const String versionAttribute = "sparkle:version";
{
if ( reader.NodeType == XmlNodeType.Element)
{
- switch(reader.Name)
+ switch (reader.Name)
{
case itemNode:
{
currentItem = new NetSparkleAppCastItem();
break;
}
+/*
+ case descriptionNode:
+ {
+ if (currentItem != null)
+ {
+ currentItem.Summary = reader.ReadElementContentAsString();
+ }
+ break;
+ }
+*/
case releaseNotesLinkNode:
{
currentItem.ReleaseNotesLink = reader.ReadString();
currentItem.ReleaseNotesLink = currentItem.ReleaseNotesLink.Trim('\n');
break;
- }
+ }
case enclosureNode:
{
currentItem.Version = reader.GetAttribute(versionAttribute);
using System.IO;
using System.Diagnostics;
using System.Reflection;
+using log4net;
namespace AppLimit.NetSparkle
{
public partial class NetSparkleDownloadProgress : Form
{
+ private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
private String _tempName;
private NetSparkleAppCastItem _item;
private String _referencedAssembly;
private void Client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
progressDownload.Visible = false;
- btnInstallAndReLaunch.Visible = true;
+ btnInstallAndReLaunch.Visible = true;
+
+ if (e.Error != null)
+ {
+ Log.Error("Update download failed ",e.Error);
+ Size = new Size(Size.Width, 137);
+ lblSecurityHint.Text = "An error occured while downloading the update. Please try again later.";
+ lblSecurityHint.Visible = true;
+ BackColor = Color.Tomato;
+ _sparkle.ReportDiagnosticMessage("Failed downloading file to: " + _tempName);
+ btnInstallAndReLaunch.Click-=btnInstallAndReLaunch_Click;
+ btnInstallAndReLaunch.Text = "Close";
+ btnInstallAndReLaunch.Click+=(s,args)=> Close();
+ btnInstallAndReLaunch.Visible = true;
+ return;
+ }
// report message
_sparkle.ReportDiagnosticMessage("Finished downloading file to: " + _tempName);
private void btnInstallAndReLaunch_Click(object sender, EventArgs e)
{
- // get the commandline
- String cmdLine = Environment.CommandLine;
- String workingDir = Environment.CurrentDirectory;
+ try
+ {
- // generate the batch file path
-
- String cmd = Environment.ExpandEnvironmentVariables("%temp%\\" + Guid.NewGuid() + ".cmd");
- String installerCMD;
+ // get the commandline
+ String cmdLine = Environment.CommandLine;
+ String workingDir = Environment.CurrentDirectory;
- // get the file type
- var extension = Path.GetExtension(_tempName).ToLower();
- switch (extension)
- {
- case ".exe":
- installerCMD = _tempName;
- break;
- case ".msi":
- installerCMD = String.Format("msiexec /i \"{0}\"",_tempName);
- break;
- default:
- MessageBox.Show("Updater not supported, please execute " + _tempName + " manually", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
- Environment.Exit(-1);
- return;
- }
+ // generate the batch file path
- // generate the batch file
- _sparkle.ReportDiagnosticMessage("Generating MSI batch in " + Path.GetFullPath(cmd));
+ String cmd = Environment.ExpandEnvironmentVariables("%temp%\\" + Guid.NewGuid() + ".cmd");
+ String installerCMD;
- using (var write = new StreamWriter(cmd))
- {
- write.WriteLine(installerCMD);
- write.WriteLine("cd " + workingDir);
- write.WriteLine(cmdLine);
- write.Close();
- }
+ // get the file type
+ var extension = Path.GetExtension(_tempName).ToLower();
+ switch (extension)
+ {
+ case ".exe":
+ installerCMD = _tempName;
+ break;
+ case ".msi":
+ installerCMD = String.Format("msiexec /i \"{0}\"", _tempName);
+ break;
+ default:
+ MessageBox.Show("Updater not supported, please execute " + _tempName + " manually", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ Environment.Exit(-1);
+ return;
+ }
- // report
- _sparkle.ReportDiagnosticMessage("Going to execute batch: " + cmd);
+ // generate the batch file
+ _sparkle.ReportDiagnosticMessage("Generating MSI batch in " + Path.GetFullPath(cmd));
- // start the installer helper
- var process = new Process();
- process.StartInfo.FileName = cmd;
- process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
- process.Start();
+ using (var write = new StreamWriter(cmd))
+ {
+ write.WriteLine(installerCMD);
+ write.WriteLine("cd " + workingDir);
+ write.WriteLine(cmdLine);
+ write.Close();
+ }
- // quit the app
- Environment.Exit(0);
+ // report
+ _sparkle.ReportDiagnosticMessage("Going to execute batch: " + cmd);
+
+ // start the installer helper
+ var process = new Process();
+ process.StartInfo.FileName = cmd;
+ process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ process.Start();
+
+ // quit the app
+ Environment.Exit(0);
+ }
+ catch (Exception exc)
+ {
+
+ Log.Error("Error while launching the update", exc);
+ this.DialogResult = DialogResult.Cancel;
+ MessageBox.Show("An error occured while executing the update.", "Update failed", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
}
}
}
lblInfoText.Text = lblInfoText.Text.Replace("APP", item.AppName + " " + item.Version);
lblInfoText.Text = lblInfoText.Text.Replace("OLDVERSION", item.AppVersionInstalled);
- if (item.ReleaseNotesLink != null && item.ReleaseNotesLink.Length > 0 )
- NetSparkleBrowser.Navigate(item.ReleaseNotesLink);
- else
- RemoveReleaseNotesControls();
+ if (!String.IsNullOrWhiteSpace(item.Summary))
+ {
+ NetSparkleBrowser.DocumentText = "<html><body>" + item.Summary + "</html></body>";
+ }
+ else
+ {
+ if (item.ReleaseNotesLink != null && item.ReleaseNotesLink.Length > 0)
+ NetSparkleBrowser.Navigate(item.ReleaseNotesLink);
+ else
+ RemoveReleaseNotesControls();
+ }
if (appIcon != null)
imgAppIcon.Image = appIcon;
<language>en</language> \r
<item> \r
<title>Version 0.7.20305</title>\r
+ <description>Looo</description>\r
<sparkle:releaseNotesLink>https://code.grnet.gr/attachments/download/965/rnotes.0.7.20305.html</sparkle:releaseNotesLink>\r
<pubDate>Sun, 05 Mar 2012 10:21:11 +0000</pubDate>\r
<enclosure \r
using Caliburn.Micro;
using Pithos.Client.WPF.Properties;
using log4net.Appender;
+using log4net.Core;
using log4net.Repository.Hierarchy;
try
{
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- var pithosDataPath= Path.Combine(appDataPath , "GRNET");
+ var pithosDataPath = Path.Combine(appDataPath, "GRNET");
if (!Directory.Exists(pithosDataPath))
Directory.CreateDirectory(pithosDataPath);
var loggerRepository = (Hierarchy)log4net.LogManager.GetRepository();
-
+
var appenders = loggerRepository.GetAppenders();
+
var lossyAppender = appenders.OfType<BufferingForwardingAppender>()
- .First(appender => appender.Name == "LossyFileAppender");
- var dumpAppender = lossyAppender.Appenders.OfType<RollingFileAppender>().First();
- dumpAppender.File = Path.Combine(pithosDataPath, "errorlog.txt");
- dumpAppender.ActivateOptions();
+ .FirstOrDefault(appender => appender.Name == "LossyFileAppender");
+ if (lossyAppender!=null)
+ {
+ var dumpAppender = lossyAppender.Appenders.OfType<RollingFileAppender>().First();
+ dumpAppender.File = Path.Combine(pithosDataPath, "errorlog.xml");
+ dumpAppender.ActivateOptions();
+ }
+
+ var debugAppender =appenders.OfType<RollingFileAppender>()
+ .FirstOrDefault(a => a.Name == "DebugFileAppender");
+ if (debugAppender != null)
+ {
+ debugAppender.File = Path.Combine(pithosDataPath, "debuglog.xml");
+ if (!Settings.Default.DebugLoggingEnabled)
+ debugAppender.Threshold = Level.Off;
+ debugAppender.ActivateOptions();
+ }
}
catch (Exception exc)
{
#endregion
using System.ComponentModel.Composition;
using System.Diagnostics;
+using System.IO;
+using System.Linq;
using Pithos.Client.WPF.Properties;
using Pithos.Interfaces;
+using log4net.Appender;
+using log4net.Core;
+using log4net.Repository.Hierarchy;
namespace Pithos.Client.WPF.Configuration
{
get { return _settings.UpdateForceCheck; }
}
+ public bool DebugLoggingEnabled
+ {
+ get { return _settings.DebugLoggingEnabled; }
+ set
+ {
+ _settings.DebugLoggingEnabled = value;
+
+ SetDebugLevel();
+ }
+ }
+
+ private static void SetDebugLevel()
+ {
+ var loggerRepository = (Hierarchy) log4net.LogManager.GetRepository();
+
+ var appenders = loggerRepository.GetAppenders();
+
+ var debugAppender = appenders.OfType<RollingFileAppender>()
+ .FirstOrDefault(a => a.Name == "DebugFileAppender");
+ if (debugAppender != null)
+ {
+ var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ var pithosDataPath = Path.Combine(appDataPath, "GRNET");
+ debugAppender.File = Path.Combine(pithosDataPath, "debuglog.xml");
+ debugAppender.Threshold = !Settings.Default.DebugLoggingEnabled ? Level.Off : Level.All;
+ debugAppender.ActivateOptions();
+ }
+ }
+
public void Save()
{
_settings.Save();
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:cal="http://www.caliburnproject.org"
xmlns:extToolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit/extended"
Title="ContainerPropertiesView" Height="500" Width="300"
- Icon="/Pithos.Client.WPF;component/Images/PithosTaskbar.png"
+ Icon="/Pithos;component/Images/PithosTaskbar.png"
Background="{StaticResource {x:Static SystemColors.ControlBrushKey}}">
<Window.Resources>
<ResourceDictionary>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
- <Image x:Name="FileIcon" Margin="5" Grid.Column="0" Stretch="None" Source="/Pithos.Client.WPF;component/Images/Container.png" />
+ <Image x:Name="FileIcon" Margin="5" Grid.Column="0" Stretch="None" Source="/Pithos;component/Images/Container.png" />
<TextBlock x:Name="ContainerName" Margin="5" Grid.Column="1" Text="Container Name" FontSize="16" FontWeight="Bold"/>
<TextBlock x:Name="ShortSize" Margin="5" Text="345 KB" FontWeight="Bold" FontSize="14" Grid.Column="2" />
</Grid>
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:cal="http://www.caliburnproject.org"
xmlns:extToolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit/extended" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" Width="400"
- Height="400" Icon="/Pithos.Client.WPF;component/Images/PithosTaskbar.png"
+ Height="400" Icon="/Pithos;component/Images/PithosTaskbar.png"
Background="{StaticResource {x:Static SystemColors.ControlBrushKey}}" WindowStartupLocation="CenterScreen" Topmost="False">
<Window.Resources>
<ResourceDictionary>
--- /dev/null
+<Window x:Class="Pithos.Client.WPF.LogConsole.LogConsoleView"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ Title="LogConsoleView" Height="300" Width="300">
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition />
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition />
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+
+ <Button x:Name="RefreshEvents" Grid.Row="0" Grid.Column="1" Content="Refresh" Margin="5"/>
+ <ListBox x:Name="Events" Grid.Row="1" Grid.ColumnSpan="2" Margin="5">
+
+ </ListBox>
+ </Grid>
+</Window>
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace Pithos.Client.WPF.LogConsole
+{
+ /// <summary>
+ /// Interaction logic for LogConsoleView.xaml
+ /// </summary>
+ public partial class LogConsoleView : Window
+ {
+ public LogConsoleView()
+ {
+ InitializeComponent();
+ }
+ }
+}
--- /dev/null
+// -----------------------------------------------------------------------
+// <copyright file="LogConsoleViewModel.cs" company="Microsoft">
+// TODO: Update copyright text.
+// </copyright>
+// -----------------------------------------------------------------------
+
+using System.ComponentModel.Composition;
+using System.Reflection;
+using Caliburn.Micro;
+using log4net.Appender;
+using log4net.Core;
+using log4net.Repository.Hierarchy;
+
+namespace Pithos.Client.WPF.LogConsole
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// TODO: Update summary.
+ /// </summary>
+ [Export(typeof(LogConsoleViewModel))]
+ public class LogConsoleViewModel:Screen
+ {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private readonly MemoryAppender _memoryAppender;
+ private IObservableCollection<LoggingEvent> _events;
+
+ public LogConsoleViewModel()
+ {
+ var loggerRepository = (Hierarchy)log4net.LogManager.GetRepository();
+
+ var appenders = loggerRepository.GetAppenders();
+
+ _memoryAppender= appenders.OfType<MemoryAppender>().FirstOrDefault();
+ if (_memoryAppender == null)
+ {
+ _memoryAppender = new MemoryAppender{Name="MemoryAppender"};
+ _memoryAppender.ActivateOptions();
+ loggerRepository.Root.AddAppender(_memoryAppender);
+ loggerRepository.RaiseConfigurationChanged(EventArgs.Empty);
+ }
+ RefreshEvents();
+ }
+
+ private void RefreshEvents()
+ {
+ Events =new BindableCollection<LoggingEvent>(_memoryAppender.GetEvents());
+ }
+
+ protected IObservableCollection<LoggingEvent> Events
+ {
+ get {
+ return _events;
+ }
+ set {
+ _events = value;
+ NotifyOfPropertyChange(()=>Events);
+ }
+ }
+ }
+}
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Pithos.Client.WPF</RootNamespace>
- <AssemblyName>Pithos.Client.WPF</AssemblyName>
+ <AssemblyName>Pithos</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<DependentUpon>NewContainerView.xaml</DependentUpon>
</Compile>
<Compile Include="FileProperties\NewContainerViewModel.cs" />
+ <Compile Include="LogConsole\LogConsoleView.xaml.cs">
+ <DependentUpon>LogConsoleView.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="LogConsole\LogConsoleViewModel.cs" />
<Compile Include="PithosException.cs" />
<Compile Include="Preferences\AccountViewModel.cs" />
<Compile Include="Preferences\AddAccountView.xaml.cs">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
+ <Page Include="LogConsole\LogConsoleView.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
<Page Include="Preferences\AddAccountView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
+ <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Title="Pithos Preferences" Height="436" Width="600"
ShowInTaskbar="true"
WindowStartupLocation="CenterScreen"
- Icon="/Pithos.Client.WPF;component/Images/PithosTaskbar.png"
+ Icon="/Pithos;component/Images/PithosTaskbar.png"
Background="{StaticResource {x:Static SystemColors.ControlBrushKey}}">
<Window.Resources>
<TabItem VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" x:Name="GeneralTab">
<TabItem.Header>
<StackPanel>
- <Image Source="/Pithos.Client.WPF;component/Images/General.png" Stretch="Uniform" Height="32"/>
+ <Image Source="/Pithos;component/Images/General.png" Stretch="Uniform" Height="32"/>
<TextBlock Text="General"/>
</StackPanel>
</TabItem.Header>
<TabItem VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" x:Name="AccountTab" IsSelected="{Binding AccountTabSelected,Mode=OneWay}">
<TabItem.Header>
<StackPanel>
- <Image Source="/Pithos.Client.WPF;component/Images/Accounts.png" Stretch="Uniform" Height="32"/>
+ <Image Source="/Pithos;component/Images/Accounts.png" Stretch="Uniform" Height="32"/>
<TextBlock Text="Accounts"/>
</StackPanel>
</TabItem.Header>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
- <Image Visibility="{Binding Converter={StaticResource BoolToVisible}, Path=IsExpired,Mode=OneWay}" Source="/Pithos.Client.WPF;component/Images/SmallWarning.png" Margin="2,0"/>
+ <Image Visibility="{Binding Converter={StaticResource BoolToVisible}, Path=IsExpired,Mode=OneWay}" Source="/Pithos;component/Images/SmallWarning.png" Margin="2,0"/>
<TextBlock Text="{Binding AccountName}" />
</StackPanel>
</DataTemplate>
<TabItem VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" Visibility="Collapsed" x:Name="RateTab">
<TabItem.Header>
<StackPanel>
- <Image Source="/Pithos.Client.WPF;component/Images/Bandwidth.png" Stretch="Uniform" Height="32"/>
+ <Image Source="/Pithos;component/Images/Bandwidth.png" Stretch="Uniform" Height="32"/>
<TextBlock Text="Bandwidth"/>
</StackPanel>
</TabItem.Header>
<TabItem VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" x:Name="ProxyTab">
<TabItem.Header>
<StackPanel>
- <Image Source="/Pithos.Client.WPF;component/Images/Network.png" Stretch="Uniform" Height="32"/>
+ <Image Source="/Pithos;component/Images/Network.png" Stretch="Uniform" Height="32"/>
<TextBlock Text="Proxy"/>
</StackPanel>
</TabItem.Header>
<TabItem VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" x:Name="AdvancedTab">
<TabItem.Header>
<StackPanel>
- <Image Source="/Pithos.Client.WPF;component/Images/Advanced.png" Stretch="Uniform" Height="32"/>
+ <Image Source="/Pithos;component/Images/Advanced.png" Stretch="Uniform" Height="32"/>
<TextBlock Text="Advanced"/>
</StackPanel>
</TabItem.Header>
- <StackPanel>
+ <WrapPanel Orientation="Vertical">
<CheckBox Content="Activate Shell Extensions" Height="16" HorizontalAlignment="Left" Margin="5" Name="ExtensionsActivated" VerticalAlignment="Top" />
<Button Content="Refresh Overlays" Name="RefreshOverlays" HorizontalAlignment="Left" Margin="5" Style="{StaticResource ButtonStyle}" Width="Auto" />
<TextBlock Text="Polling Interval (secs)" Margin="5"/>
<extToolkit:IntegerUpDown x:Name="Settings_HashingParallelism" HorizontalAlignment="Left" Width="100" Margin="5,0" Watermark="Enter number of tasks" Minimum="1" />
<TextBlock Text="Startup Delay (minutes)" Margin="5"/>
<extToolkit:IntegerUpDown x:Name="StartupDelay" HorizontalAlignment="Left" Width="100" Margin="5,0" Watermark="Enter number of tasks" Minimum="0" />
+ <CheckBox Content="Enable Debug Logging" Height="16" HorizontalAlignment="Left" Margin="5,10,5,5" Name="DebugLoggingEnabled" VerticalAlignment="Top"/>
<Button x:Name="OpenLogPath" Content="Open Log Path" HorizontalAlignment="Left" Margin="5" Style="{StaticResource ButtonStyle}" Width="Auto"/>
- </StackPanel>
+ <Button x:Name="OpenLogConsole" Content="Open Log Console" HorizontalAlignment="Left" Margin="5" Style="{StaticResource ButtonStyle}" Width="Auto" Visibility="Hidden"/>
+ </WrapPanel>
</TabItem>
</TabControl>
{
Shell.OpenLogPath();
}
+
+ public void OpenLogConsole()
+ {
+ var logView=IoC.Get<LogConsole.LogConsoleViewModel>();
+ _windowManager.ShowWindow(logView);
+ }
+
public void SaveChanges()
{
DoSave();
}
}
+ public bool DebugLoggingEnabled
+ {
+ get { return Settings.DebugLoggingEnabled; }
+ set {
+ Settings.DebugLoggingEnabled = value;
+ NotifyOfPropertyChange(()=>DebugLoggingEnabled);
+ }
+ }
#endregion
[assembly: AssemblyCopyright("Copyright © GRNet 2011-2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyInformationalVersion("2012-03-07")]
+[assembly: AssemblyInformationalVersion("2012-03-12")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.7.20305.1")]
-[assembly: AssemblyFileVersionAttribute("0.7.20305.1")]
+[assembly: AssemblyVersion("0.7.20307.0")]
+[assembly: AssemblyFileVersionAttribute("0.7.20307.0")]
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.WebServiceUrl)]
- [global::System.Configuration.DefaultSettingValueAttribute("https://github.com/pkanavos/PithosUpdateTest/raw/master/versioninfo.xml")]
+ [global::System.Configuration.DefaultSettingValueAttribute("https://raw.github.com/pkanavos/PithosUpdateTest/master/versioninfo.xml")]
public string UpdateUrl {
get {
return ((string)(this["UpdateUrl"]));
return ((bool)(this["UpdateForceCheck"]));
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool DebugLoggingEnabled {
+ get {
+ return ((bool)(this["DebugLoggingEnabled"]));
+ }
+ set {
+ this["DebugLoggingEnabled"] = value;
+ }
+ }
}
}
<Value Profile="(Default)">00:01:00</Value>
</Setting>
<Setting Name="UpdateUrl" Type="(Web Service URL)" Scope="Application">
- <Value Profile="(Default)">https://github.com/pkanavos/PithosUpdateTest/raw/master/versioninfo.xml</Value>
+ <Value Profile="(Default)">https://raw.github.com/pkanavos/PithosUpdateTest/master/versioninfo.xml</Value>
</Setting>
<Setting Name="UpdateDiagnostics" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
<Setting Name="UpdateForceCheck" Type="System.Boolean" Scope="Application">
<Value Profile="(Default)">True</Value>
</Setting>
+ <Setting Name="DebugLoggingEnabled" Type="System.Boolean" Scope="User">
+ <Value Profile="(Default)">False</Value>
+ </Setting>
</Settings>
</SettingsFile>
\ No newline at end of file
Title="Selective Synch" Height="300" Width="300"
ShowInTaskbar="true"
WindowStartupLocation="CenterScreen"
- Icon="/Pithos.Client.WPF;component/Images/PithosTaskbar.png"
+ Icon="/Pithos;component/Images/PithosTaskbar.png"
Background="{StaticResource {x:Static SystemColors.ControlBrushKey}}">
<Window.Resources>
<ResourceDictionary>
<Window x:Class="Pithos.Client.WPF.Shell.AboutView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="AboutView" Height="300" Width="300" Icon="/Pithos.Client.WPF;component/Images/PithosTaskbar.png">
+ Title="AboutView" Height="300" Width="300" Icon="/Pithos;component/Images/PithosTaskbar.png">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
- <Image Grid.Row="0" Source="/Pithos.Client.WPF;component/Images/SmallLogo.png" />
+ <Image Grid.Row="0" Source="/Pithos;component/Images/SmallLogo.png" />
<TextBlock Grid.Row="0" Text="Pithos " HorizontalAlignment="Center" FontSize="28" FontWeight="Bold" FontFamily="Segoe UI" Width="200" TextWrapping="WrapWithOverflow" TextAlignment="Center" VerticalAlignment="Center"/>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="10,0">
<TextBlock Text="Version" VerticalAlignment="Center"/>
\r
if (!_iconCache.TryGetValue(icon, out image))\r
{\r
- var imagePath = String.Format("/Pithos.Client.WPF;component/Images/{0}.png", Enum.GetName(typeof(BalloonIcon), value));\r
+ var imagePath = String.Format("/Pithos;component/Images/{0}.png", Enum.GetName(typeof(BalloonIcon), value));\r
var uri = new Uri(imagePath, UriKind.Relative);\r
image=new BitmapImage(uri);\r
_iconCache[icon] = image;\r
<Window x:Class="Pithos.Client.WPF.Shell.FeedbackView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="FeedbackView" Height="389" Width="455" xmlns:my="clr-namespace:Pithos.Client.WPF.Converters" Icon="/Pithos.Client.WPF;component/Images/PithosTaskbar.png">
+ Title="FeedbackView" Height="389" Width="455" xmlns:my="clr-namespace:Pithos.Client.WPF.Converters" Icon="/Pithos;component/Images/PithosTaskbar.png">
<Window.Resources>
<my:EmptyToVisibilityConverter x:Key="EmptyToVisible" />
</Window.Resources>
Title="MessageView" Height="Auto" Width="500"
Background="{StaticResource {x:Static SystemColors.ControlBrushKey}}"
WindowStartupLocation="CenterScreen"
- Icon="/Pithos.Client.WPF;component/Images/PithosTaskbar.png"
+ Icon="/Pithos;component/Images/PithosTaskbar.png"
SizeToContent="Height" xmlns:my="clr-namespace:Microsoft.Windows.Controls.Core.Converters;assembly=WPFToolkit.Extended">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close"
xmlns:cal="http://www.caliburnproject.org"
x:Name="TheView" WindowStartupLocation="CenterScreen" WindowStyle="None"
Visibility="Collapsed"
- Width="700" Height="200" SizeToContent="Width" WindowState="Minimized" Icon="/Pithos.Client.WPF;component/Images/PithosTaskbar.png" xmlns:my="clr-namespace:Pithos.Client.WPF.Converters">
+ Width="700" Height="200" SizeToContent="Width" WindowState="Minimized" Icon="/Pithos;component/Images/PithosTaskbar.png" xmlns:my="clr-namespace:Pithos.Client.WPF.Converters">
<!--
<Window.Background>
</DataTemplate>
</MenuItem.ItemTemplate>
<MenuItem.Icon>
- <Image Source="/Pithos.Client.WPF;component/Images/Folder.ico" />
+ <Image Source="/Pithos;component/Images/Folder.ico" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Go to Account Site" x:Name="GoToSiteMenu" ItemsSource="{Binding Accounts}" Visibility="{Binding Path=HasAccounts, Converter={StaticResource BooleanToVisible}}" >
</DataTemplate>
</MenuItem.ItemTemplate>
<MenuItem.Icon>
- <Image Source="/Pithos.Client.WPF;component/Images/Web.ico" />
+ <Image Source="/Pithos;component/Images/Web.ico" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Send Feedback" x:Name="SendFeedback" cal:Message.Attach="SendFeedback">
<MenuItem.Icon>
- <Image Source="/Pithos.Client.WPF;component/Images/Feedback.ico" />
+ <Image Source="/Pithos;component/Images/Feedback.ico" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="About Pithos" x:Name="AboutPithos" cal:Message.Attach="AboutPithos">
<MenuItem.Icon>
- <Image Source="/Pithos.Client.WPF;component/Images/About.ico" />
+ <Image Source="/Pithos;component/Images/About.ico" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Check For Upgrade" x:Name="CheckForUpgrade" cal:Message.Attach="CheckForUpgrade"/>
</DataTemplate>
</MenuItem.HeaderTemplate>
<MenuItem.Icon>
- <Image Source="/Pithos.Client.WPF;component/Images/Web.ico" />
+ <Image Source="/Pithos;component/Images/Web.ico" />
</MenuItem.Icon>
</MenuItem>
<Separator />
</tb:TaskbarIcon.TrayToolTip>
</tb:TaskbarIcon>
<!--
- <Image Margin="0,0,10,5" Source="/Pithos.Client.WPF;component/Images/logo.png" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stretch="None"/>
+ <Image Margin="0,0,10,5" Source="/Pithos;component/Images/logo.png" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stretch="None"/>
-->
</Grid>
</Window>
public void CheckForUpgrade()
{
+ Log.Error("Test Error message");
_sparkle.StopLoop();
_sparkle.Dispose();
_sparkle=new Sparkle(Settings.UpdateUrl);
</dependentAssembly>
</assemblyBinding>
</runtime>
+ <!--<appSettings>
+ <add key="log4net.Internal.Debug" value="true" />
+ </appSettings>
+ <system.diagnostics>
+ <trace autoflush="true">
+ <listeners>
+ <add name="textWriterTraceListener"
+ type="System.Diagnostics.TextWriterTraceListener"
+ initializeData="e:\\temp\\log4net.txt" />
+ </listeners>
+ </trace>
+ </system.diagnostics>-->
<userSettings>
<Pithos.Client.WPF.Properties.Settings>
<setting name="PithosPath" serializeAs="String">
<setting name="UpdateCheckInterval" serializeAs="String">
<value>24.00:00:00</value>
</setting>
+ <setting name="DebugLoggingEnabled" serializeAs="String">
+ <value>False</value>
+ </setting>
</Pithos.Client.WPF.Properties.Settings>
</userSettings>
<connectionStrings>
<value>https://pithos.dev.grnet.gr</value>
</setting>
<setting name="UpdateUrl" serializeAs="String">
- <value>https://github.com/pkanavos/PithosUpdateTest/raw/master/versioninfo.xml</value>
+ <value>https://raw.github.com/pkanavos/PithosUpdateTest/master/versioninfo.xml</value>
</setting>
<setting name="FileIdleTimeout" serializeAs="String">
<value>00:00:10</value>
<layout type="log4net.Layout.XMLLayout"/>
</appender>
+ <appender name="DebugFileAppender" type="log4net.Appender.RollingFileAppender">
+ <file value="debuglog.xml" />
+ <appendToFile value="true" />
+ <rollingStyle value="Size" />
+ <maxSizeRollBackups value="10" />
+ <maximumFileSize value="2MB" />
+ <staticLogFileName value="true" />
+ <layout type="log4net.Layout.XMLLayout"/>
+ </appender>
+
<appender name="OutputDebugStringAppender" type="log4net.Appender.OutputDebugStringAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger (%property{Operation}) [%level]- %message%newline" />
<threshold value="ERROR" />
</evaluator>
<appender-ref ref="DumpFileAppender" />
+ </appender>
+<!--
+ <appender name="LossySmtpAppender" type="log4net.Appender.SmtpAppender">
+ <to value="pkanavos@gmail.com" />
+ <from value="pkpithos@gmail.com" />
+ <subject value="Some subject" />
+ <smtpHost value="smtp.gmail.com" />
+ <authentication value="Basic" />
+ <port value="587" />
+ <bufferSize value="30" />
+ <EnableSsl value="true"/>
+ <lossy value="true" />
+ <evaluator type="log4net.Core.LevelEvaluator">
+ <threshold value="ERROR"/>
+ </evaluator>
+ <layout type="log4net.Layout.PatternLayout">
+ <conversionPattern value="%newline%date [%thread] %-5level %logger [%property{Operation}] - %message%newline%newline%newline" />
+ </layout>
</appender>
+-->
<logger name="NHibernate" additivity="false">
<level value="WARN"/>
<appender-ref ref="TraceAppender"/>
</logger>
+<!--
<logger name="Caliburn" additivity="false">
<level value="WARN"/>
<appender-ref ref="TraceAppender"/>
</logger>
+-->
<logger name="Pithos" additivity="false">
<level value="DEBUG"/>
<root>
<level value="DEBUG" />
+ <appender-ref ref="DebugFileAppender" />
+<!--
<appender-ref ref="LossyFileAppender" />
+ <appender-ref ref="LossySmtpAppender" />
+-->
<appender-ref ref="TraceAppender" />
<appender-ref ref="OutputDebugStringAppender" />
</root>
if (cloudFile.Content_Type == @"application/directory")
{
if (!Directory.Exists(localPath))
- Directory.CreateDirectory(localPath);
+ try
+ {
+ Directory.CreateDirectory(localPath);
+ if (Log.IsDebugEnabled)
+ Log.DebugFormat("Created Directory [{0}]",localPath);
+ }
+ catch (IOException)
+ {
+ var localInfo = new FileInfo(localPath);
+ if (localInfo.Exists && localInfo.Length == 0)
+ {
+ Log.WarnFormat("Malformed directory object detected for [{0}]",localPath);
+ localInfo.Delete();
+ Directory.CreateDirectory(localPath);
+ if (Log.IsDebugEnabled)
+ Log.DebugFormat("Created Directory [{0}]", localPath);
+ }
+ }
}
else
{
await
DownloadEntireFileAsync(accountInfo, client, cloudFile, relativeUrl, localPath,
serverHash);
- //Otherwise download it block by block
+ //Otherwise download it block by block
else
await
DownloadWithBlocks(accountInfo, client, cloudFile, relativeUrl, localPath,
//Create the local folder if it doesn't exist (necessary for shared objects)
var localFolder = Path.GetDirectoryName(localPath);
if (!Directory.Exists(localFolder))
- Directory.CreateDirectory(localFolder);
+ try
+ {
+ Directory.CreateDirectory(localFolder);
+ }
+ catch (IOException)
+ {
+ //A file may already exist that has the same name as the new folder.
+ //This may be an artifact of the way Pithos handles directories
+ var fileInfo = new FileInfo(localFolder);
+ if (fileInfo.Exists && fileInfo.Length == 0)
+ {
+ fileInfo.Delete();
+ Directory.CreateDirectory(localFolder);
+ }
+ else
+ throw;
+ }
//And move it to its actual location once downloading is finished
if (File.Exists(localPath))
File.Replace(tempPath,localPath,null,true);
//
if (action.FileState == null)
action.FileState = StatusKeeper.GetStateByFilePath(fileInfo.FullName);
+ if (action.FileState == null)
+ {
+ Log.WarnFormat("File [{0}] has no local state. It was probably created by a download action",fileInfo.FullName);
+ return;
+ }
//Do not upload files in conflict
if (action.FileState.FileStatus == FileStatus.Conflict )
{
{\r
UpdateStatus(PithosStatus.PollSyncing);\r
\r
- //Next time we will check for all changes since the current check minus 1 second\r
- //This is done to ensure there are no discrepancies due to clock differences\r
- var current = DateTime.Now.AddSeconds(-1);\r
-\r
var tasks = from accountInfo in _accounts.Values\r
select ProcessAccountFiles(accountInfo, since);\r
\r
- await TaskEx.WhenAll(tasks.ToList());\r
+ var nextTimes=await TaskEx.WhenAll(tasks.ToList());\r
\r
_firstPoll = false;\r
//Reschedule the poll with the current timestamp as a "since" value\r
- nextSince = current;\r
+\r
+ if (nextTimes.Length>0)\r
+ nextSince = nextTimes.Min();\r
+ if (Log.IsDebugEnabled)\r
+ Log.DebugFormat("Next Poll at [{0}]",nextSince);\r
}\r
catch (Exception ex)\r
{\r
return since;\r
}\r
\r
- public async Task ProcessAccountFiles(AccountInfo accountInfo, DateTime? since = null)\r
+ public async Task<DateTime?> ProcessAccountFiles(AccountInfo accountInfo, DateTime? since = null)\r
{\r
if (accountInfo == null)\r
throw new ArgumentNullException("accountInfo");\r
\r
using (log4net.ThreadContext.Stacks["Retrieve Remote"].Push(accountInfo.UserName))\r
{\r
+\r
await NetworkAgent.GetDeleteAwaiter();\r
\r
Log.Info("Scheduled");\r
\r
CreateContainerFolders(accountInfo, containers);\r
\r
+ //The nextSince time fallback time is the same as the current.\r
+ //If polling succeeds, the next Since time will be the smallest of the maximum modification times\r
+ //of the shared and account objects\r
+ var nextSince = since;\r
+\r
try\r
{\r
//Wait for any deletions to finish\r
var dict = listTasks.ToDictionary(t => t.AsyncState);\r
\r
//Get all non-trash objects. Remember, the container name is stored in AsyncState\r
- var remoteObjects = from objectList in listTasks\r
+ var remoteObjects = (from objectList in listTasks\r
where (string)objectList.AsyncState != "trash"\r
from obj in objectList.Result\r
- select obj;\r
+ select obj).ToList();\r
+ \r
+ //Get the latest remote object modification date, only if it is after\r
+ //the original since date\r
+ nextSince = GetLatestDateAfter(nextSince, remoteObjects);\r
\r
var sharedObjects = dict["shared"].Result;\r
+ nextSince = GetLatestDateBefore(nextSince, sharedObjects);\r
\r
//DON'T process trashed files\r
//If some files are deleted and added again to a folder, they will be deleted\r
catch (Exception ex)\r
{\r
Log.ErrorFormat("[FAIL] ListObjects for{0} in ProcessRemoteFiles with {1}", accountInfo.UserName, ex);\r
- return;\r
+ return nextSince;\r
}\r
\r
Log.Info("[LISTENER] Finished");\r
-\r
+ return nextSince;\r
}\r
}\r
\r
+ /// <summary>\r
+ /// Returns the latest LastModified date from the list of objects, but only if it is before\r
+ /// than the threshold value\r
+ /// </summary>\r
+ /// <param name="threshold"></param>\r
+ /// <param name="cloudObjects"></param>\r
+ /// <returns></returns>\r
+ private static DateTime? GetLatestDateBefore(DateTime? threshold, IList<ObjectInfo> cloudObjects)\r
+ {\r
+ DateTime? maxDate = null;\r
+ if (cloudObjects!=null && cloudObjects.Count > 0)\r
+ maxDate = cloudObjects.Max(obj => obj.Last_Modified);\r
+ if (maxDate == null || maxDate == DateTime.MinValue)\r
+ return threshold;\r
+ if (threshold == null || threshold == DateTime.MinValue || threshold > maxDate)\r
+ return maxDate;\r
+ return threshold;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Returns the latest LastModified date from the list of objects, but only if it is after\r
+ /// the threshold value\r
+ /// </summary>\r
+ /// <param name="threshold"></param>\r
+ /// <param name="cloudObjects"></param>\r
+ /// <returns></returns>\r
+ private static DateTime? GetLatestDateAfter(DateTime? threshold, IList<ObjectInfo> cloudObjects)\r
+ {\r
+ DateTime? maxDate = null;\r
+ if (cloudObjects!=null && cloudObjects.Count > 0)\r
+ maxDate = cloudObjects.Max(obj => obj.Last_Modified);\r
+ if (maxDate == null || maxDate == DateTime.MinValue)\r
+ return threshold;\r
+ if (threshold == null || threshold == DateTime.MinValue || threshold < maxDate)\r
+ return maxDate;\r
+ return threshold;\r
+ }\r
+\r
readonly AccountsDifferencer _differencer = new AccountsDifferencer();\r
private List<Uri> _selectiveUris=new List<Uri>();\r
\r
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;
using Castle.ActiveRecord.Framework.Config;
+using NHibernate.ByteCode.Castle;
+using NHibernate.Cfg;
+using NHibernate.Cfg.Loquacious;
+using NHibernate.Dialect;
using Pithos.Interfaces;
using Pithos.Network;
using log4net;
+using Environment = System.Environment;
namespace Pithos.Core.Agents
{
public StatusAgent()
{
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
-
-
_pithosDataPath = Path.Combine(appDataPath , "GRNET");
if (!Directory.Exists(_pithosDataPath))
MigrateOldDb(dbPath, appDataPath);
+
var source = GetConfiguration(_pithosDataPath);
ActiveRecordStarter.Initialize(source,typeof(FileState),typeof(FileTag));
+
ActiveRecordStarter.UpdateSchema();
client.Headers.Add("X-Move-From", sourceUrl);
client.AllowedStatusCodes.Add(HttpStatusCode.NotFound);
+ Log.InfoFormat("[TRASH] [{0}] to [{1}]",sourceUrl,targetUrl);
client.PutWithRetry(targetUrl, 3);
var expectedCodes = new[] {HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created,HttpStatusCode.NotFound};
}
catch (WebException exc)
{
- Log.WarnFormat("[{0}] {1} {2}", request.Method, exc.Status, request.RequestUri);
- if (!TryGetResponse(exc, out response))
+ if (!TryGetResponse(exc, request,out response))
throw;
}
}
catch (WebException exc)
{
- Log.WarnFormat("[{0}] {1} {2}", request.Method, exc.Status, request.RequestUri);
- if (!TryGetResponse(exc, out response))
+ if (!TryGetResponse(exc, request,out response))
throw;
}
return response;
}
- private bool TryGetResponse(WebException exc, out HttpWebResponse response)
+ private bool TryGetResponse(WebException exc, WebRequest request,out HttpWebResponse response)
{
response = null;
//Fail on empty response
if (exc.Response == null)
+ {
+ Log.WarnFormat("[{0}] {1} {2}", request.Method, exc.Status, request.RequestUri);
return false;
+ }
response = (exc.Response as HttpWebResponse);
+ var statusCode = (int)response.StatusCode;
//Succeed on allowed status codes
if (AllowedStatusCodes.Contains(response.StatusCode))
+ {
+ if (Log.IsDebugEnabled)
+ Log.DebugFormat("[{0}] {1} {2}", request.Method, statusCode, request.RequestUri);
return true;
+ }
+
+ Log.WarnFormat("[{0}] {1} {2}", request.Method, statusCode, request.RequestUri);
//Does the response have any content to log?
if (exc.Response.ContentLength > 0)
}
"Entry"
{
- "MsmKey" = "8:_A952570414DA4B8547D9CD8FEE174CD1"
- "OwnerKey" = "8:_1BD6A9CD577C40098C968C8B464A03BC"
- "MsmSig" = "8:_UNDEFINED"
- }
- "Entry"
- {
"MsmKey" = "8:_B998E7FC1F540278910B0D58694455C2"
"OwnerKey" = "8:_4C5B93BC82FE5E63E01458A8DA46B4D6"
"MsmSig" = "8:_UNDEFINED"
"IsDependency" = "11:TRUE"
"IsolateTo" = "8:"
}
- "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A952570414DA4B8547D9CD8FEE174CD1"
- {
- "SourcePath" = "8:Pithos.ShellExtensions.tlb"
- "TargetName" = "8:Pithos.ShellExtensions.tlb"
- "Tag" = "8:"
- "Folder" = "8:_FA3E4362540D4C76A5914763C178A3BD"
- "Condition" = "8:"
- "Transitive" = "11:FALSE"
- "Vital" = "11:TRUE"
- "ReadOnly" = "11:FALSE"
- "Hidden" = "11:FALSE"
- "System" = "11:FALSE"
- "Permanent" = "11:FALSE"
- "SharedLegacy" = "11:FALSE"
- "PackageAs" = "3:1"
- "Register" = "3:2"
- "Exclude" = "11:FALSE"
- "IsDependency" = "11:TRUE"
- "IsolateTo" = "8:"
- }
"{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_B998E7FC1F540278910B0D58694455C2"
{
"AssemblyRegister" = "3:1"
"Name" = "8:Microsoft Visual Studio"
"ProductName" = "8:Pithos"
"ProductCode" = "8:{B83227E8-A064-475F-9BC8-38BBFBC55AB8}"
- "PackageCode" = "8:{7FE3E24C-8949-4EE2-9401-B9E36F09697E}"
+ "PackageCode" = "8:{B73DD858-2392-40D4-96FE-28CEFF8F6F42}"
"UpgradeCode" = "8:{205365D1-28AA-4322-A46C-FCB37502C6EF}"
"AspNetVersion" = "8:4.0.30319.0"
"RestartWWWService" = "11:FALSE"
"Name" = "8:Uninstall Pithos"
"Arguments" = "8:[ProductCode]"
"Description" = "8:"
- "ShowCmd" = "3:1"
+ "ShowCmd" = "3:7"
"IconIndex" = "3:32512"
"Transitive" = "11:FALSE"
"Target" = "8:_220D09CEABB240EFBF5BB44F9D29E4C7"
"Name" = "8:Uninstall Pithos"
"Arguments" = "8:[ProductCode]"
"Description" = "8:"
- "ShowCmd" = "3:1"
+ "ShowCmd" = "3:7"
"IconIndex" = "3:32512"
"Transitive" = "11:FALSE"
"Target" = "8:_F73E036AFAC54F8DB99B8194DE4F11A9"