Merge branch 'master' into gettext-cs

Conflicts:
	SparkleLib/Git/SparkleRepoGit.cs
This commit is contained in:
serras 2012-02-05 18:54:32 +01:00
commit d096c39e5d
31 changed files with 559 additions and 342 deletions

11
NEWS
View file

@ -1,3 +1,14 @@
0.8.1 for Linux and Mac (Sun Jan 29):
Hylke:
- Disable the Quit menu item when a sync is going on
- Show remote url when hovering project names in the log
- Fix the event log on Linux
- Allow server ports to be specified once again
- Open Folder button after setup now takes you to SparkleShare/subfolder
- Close event log on Cmd+W on Mac
0.8.0 for Linux and Mac (Sun Jan 22):
Hylke:

View file

@ -51,7 +51,7 @@ namespace SparkleLib {
if (!uri.Scheme.Equals ("ssh") &&
!uri.Scheme.Equals ("git")) {
uri = new Uri ("ssh://" + server);
uri = new Uri ("ssh://" + uri);
}
@ -72,8 +72,10 @@ namespace SparkleLib {
uri = new Uri ("ssh://git@gnome.org/git" + uri.AbsolutePath);
} else {
if (string.IsNullOrEmpty (uri.UserInfo))
uri = new Uri (uri.Scheme + "://git@" + uri.Host + uri.AbsolutePath);
if (string.IsNullOrEmpty (uri.UserInfo)) {
uri = new Uri (uri.Scheme + "://git@" + uri.Host + ":" + uri.Port + uri.AbsolutePath);
uri = new Uri (uri.ToString ().Replace (":-1", ""));
}
}
@ -221,75 +223,11 @@ namespace SparkleLib {
SparkleHelpers.CombineMore (this.target_folder, ".git", "info"));
// File that lists the files we want git to ignore
string exlude_rules_file_path = Path.Combine (info.FullName, "exclude");
TextWriter writer = new StreamWriter (exlude_rules_file_path);
string exclude_rules_file_path = Path.Combine (info.FullName, "exclude");
TextWriter writer = new StreamWriter (exclude_rules_file_path);
// gedit and emacs
writer.WriteLine ("*~");
// Firefox and Chromium temporary download files
writer.WriteLine ("*.part");
writer.WriteLine ("*.crdownload");
// vi(m)
writer.WriteLine (".*.sw[a-z]");
writer.WriteLine ("*.un~");
writer.WriteLine ("*.swp");
writer.WriteLine ("*.swo");
// KDE
writer.WriteLine (".directory");
// Mac OS X
writer.WriteLine (".DS_Store");
writer.WriteLine ("Icon?");
writer.WriteLine ("._*");
writer.WriteLine (".Spotlight-V100");
writer.WriteLine (".Trashes");
// Omnigraffle
writer.WriteLine ("*(Autosaved).graffle");
// Windows
writer.WriteLine ("Thumbs.db");
writer.WriteLine ("Desktop.ini");
// MS Office
writer.WriteLine ("~*.tmp");
writer.WriteLine ("~*.TMP");
writer.WriteLine ("*~*.tmp");
writer.WriteLine ("*~*.TMP");
writer.WriteLine ("~*.ppt");
writer.WriteLine ("~*.PPT");
writer.WriteLine ("~*.pptx");
writer.WriteLine ("~*.PPTX");
writer.WriteLine ("~*.xls");
writer.WriteLine ("~*.XLS");
writer.WriteLine ("~*.xlsx");
writer.WriteLine ("~*.XLSX");
writer.WriteLine ("~*.doc");
writer.WriteLine ("~*.DOC");
writer.WriteLine ("~*.docx");
writer.WriteLine ("~*.DOCX");
// CVS
writer.WriteLine ("*/CVS/*");
writer.WriteLine (".cvsignore");
writer.WriteLine ("*/.cvsignore");
// Subversion
writer.WriteLine ("/.svn/*");
writer.WriteLine ("*/.svn/*");
// Mercurial
writer.WriteLine ("/.hg/*");
writer.WriteLine ("*/.hg/*");
writer.WriteLine ("*/.hgignore");
// Bazaar
writer.WriteLine ("/.bzr/*");
writer.WriteLine ("*/.bzr/*");
writer.WriteLine ("*/.bzrignore");
foreach (string exclude_rule in ExcludeRules)
writer.WriteLine (exclude_rule);
writer.Close ();

View file

@ -32,6 +32,7 @@ namespace SparkleLib {
StartInfo.RedirectStandardOutput = true;
StartInfo.UseShellExecute = false;
StartInfo.WorkingDirectory = path;
StartInfo.CreateNoWindow = true;
if (!string.IsNullOrEmpty (ExecPath))
StartInfo.Arguments = "--exec-path=\"" + ExecPath + "\" " + args;

View file

@ -26,8 +26,9 @@ namespace SparkleLib {
public class SparkleRepoGit : SparkleRepoBase {
public SparkleRepoGit (string path, SparkleBackend backend) :
base (path, backend) { }
public SparkleRepoGit (string path) : base (path)
{
}
private string identifier = null;
@ -71,7 +72,7 @@ namespace SparkleLib {
public override double Size {
get {
string file_path = SparkleHelpers.CombineMore (LocalPath, ".git", "repo_size");
string file_path = new string [] {LocalPath, ".git", "repo_size"}.Combine ();
try {
return double.Parse (File.ReadAllText (file_path));
@ -85,7 +86,7 @@ namespace SparkleLib {
public override double HistorySize {
get {
string file_path = SparkleHelpers.CombineMore(LocalPath, ".git", "repo_history_size");
string file_path = new string [] {LocalPath, ".git", "repo_history_size"}.Combine ();
try {
return double.Parse (File.ReadAllText (file_path));
@ -105,8 +106,8 @@ namespace SparkleLib {
double history_size = CalculateSize (
new DirectoryInfo (Path.Combine (LocalPath, ".git")));
string size_file_path = SparkleHelpers.CombineMore(LocalPath, ".git", "repo_size");
string history_size_file_path = SparkleHelpers.CombineMore(LocalPath, ".git", "repo_history_size");
string size_file_path = new string [] {LocalPath, ".git", "repo_size"}.Combine ();
string history_size_file_path = new string [] {LocalPath, ".git", "repo_history_size"}.Combine ();
File.WriteAllText (size_file_path, size.ToString ());
File.WriteAllText (history_size_file_path, history_size.ToString ());
@ -169,7 +170,7 @@ namespace SparkleLib {
public override bool CheckForRemoteChanges ()
{
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Checking for remote changes...");
SparkleGit git = new SparkleGit (LocalPath, "ls-remote origin master");
SparkleGit git = new SparkleGit (LocalPath, "ls-remote " + Url + " master");
git.Start ();
git.WaitForExit ();
@ -201,7 +202,7 @@ namespace SparkleLib {
SparkleGit git = new SparkleGit (LocalPath,
"push --progress " + // Redirects progress stats to standarderror
"origin master");
Url + " master");
git.StartInfo.RedirectStandardError = true;
git.Start ();
@ -258,7 +259,7 @@ namespace SparkleLib {
public override bool SyncDown ()
{
SparkleGit git = new SparkleGit (LocalPath, "fetch --progress");
SparkleGit git = new SparkleGit (LocalPath, "fetch --progress " + Url);
git.StartInfo.RedirectStandardError = true;
git.Start ();
@ -623,11 +624,12 @@ namespace SparkleLib {
if (match.Success) {
SparkleChangeSet change_set = new SparkleChangeSet ();
change_set.Folder = Name;
change_set.Revision = match.Groups [1].Value;
change_set.User.Name = match.Groups [2].Value;
change_set.User.Email = match.Groups [3].Value;
change_set.IsMagical = is_merge_commit;
change_set.Folder = Name;
change_set.Revision = match.Groups [1].Value;
change_set.User.Name = match.Groups [2].Value;
change_set.User.Email = match.Groups [3].Value;
change_set.IsMagical = is_merge_commit;
change_set.Url = Url;
change_set.Timestamp = new DateTime (int.Parse (match.Groups [4].Value),
int.Parse (match.Groups [5].Value), int.Parse (match.Groups [6].Value),

View file

@ -9,6 +9,7 @@ SOURCES = \
SparkleBackend.cs \
SparkleChangeSet.cs \
SparkleConfig.cs \
SparkleExtensions.cs \
SparkleFetcherBase.cs \
SparkleHelpers.cs \
SparkleListenerBase.cs \

View file

@ -30,6 +30,7 @@ namespace SparkleLib {
public DateTime Timestamp;
public DateTime FirstTimestamp;
public bool IsMagical = false;
public Uri Url;
public List<string> Added = new List<string> ();
public List<string> Deleted = new List<string> ();

35
SparkleLib/SparkleExtensions.cs Executable file
View file

@ -0,0 +1,35 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons (hylkebons@gmail.com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see (http://www.gnu.org/licenses/).
using System;
using System.IO;
namespace SparkleLib {
public static class Extensions {
public static string Combine (this String [] parts)
{
string new_path = "";
foreach (string part in parts)
new_path = Path.Combine (new_path, part);
return new_path;
}
}
}

View file

@ -16,8 +16,8 @@
using System;
using System.IO;
using System.Diagnostics;
using System.IO;
using System.Security.AccessControl;
using System.Text.RegularExpressions;
using System.Threading;
@ -37,22 +37,94 @@ namespace SparkleLib {
public event FailedEventHandler Failed;
public event ProgressChangedEventHandler ProgressChanged;
public string [] ExcludeRules;
protected string target_folder;
protected string remote_url;
private Thread thread;
public SparkleFetcherBase (string server, string remote_folder, string target_folder)
{
this.target_folder = target_folder;
this.remote_url = server + "/" + remote_folder;
ExcludeRules = new string [] {
// gedit and emacs
"*~",
// Firefox and Chromium temporary download files
"*.part",
"*.crdownload",
// vi(m)
".*.sw[a-z]",
"*.un~",
"*.swp",
"*.swo",
// KDE
".directory",
// Mac OS X
".DS_Store",
"Icon?",
"._*",
".Spotlight-V100",
".Trashes",
// Omnigraffle
"*(Autosaved).graffle",
// Windows
"Thumbs.db",
"Desktop.ini",
// MS Office
"~*.tmp",
"~*.TMP",
"*~*.tmp",
"*~*.TMP",
"~*.ppt",
"~*.PPT",
"~*.pptx",
"~*.PPTX",
"~*.xls",
"~*.XLS",
"~*.xlsx",
"~*.XLSX",
"~*.doc",
"~*.DOC",
"~*.docx",
"~*.DOCX",
// CVS
"*/CVS/*",
".cvsignore",
"*/.cvsignore",
// Subversion
"/.svn/*",
"*/.svn/*",
// Mercurial
"/.hg/*",
"*/.hg/*",
"*/.hgignore",
// Bazaar
"/.bzr/*",
"*/.bzr/*",
"*/.bzrignore"
};
}
public abstract bool Fetch ();
public abstract string [] Warnings { get; }
// Clones the remote repository
public void Start ()
{

View file

@ -49,6 +49,7 @@
<Compile Include="SparkleBackend.cs" />
<Compile Include="SparkleConfig.cs" />
<Compile Include="SparkleWatcher.cs" />
<Compile Include="SparkleExtensions.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>

View file

@ -64,7 +64,7 @@ namespace SparkleLib {
SparkleHelpers.DebugInfo ("ListenerFactory",
"Refered to existing listener for " + announce_uri);
listener.AlsoListenTo (folder_identifier);
listener.AlsoListenToBase (folder_identifier);
return (SparkleListenerBase) listener;
}
}
@ -90,40 +90,52 @@ namespace SparkleLib {
// listens for change notifications
public abstract class SparkleListenerBase {
// We've connected to the server
public event ConnectedEventHandler Connected;
public delegate void ConnectedEventHandler ();
// We've disconnected from the server
public event DisconnectedEventHandler Disconnected;
public delegate void DisconnectedEventHandler ();
// We've been notified about a remote
// change by the channel
public event AnnouncementEventHandler Announcement;
public delegate void AnnouncementEventHandler (SparkleAnnouncement announcement);
public event ReceivedEventHandler Received;
public delegate void ReceivedEventHandler (SparkleAnnouncement announcement);
public readonly Uri Server;
public abstract void Connect ();
public abstract void Announce (SparkleAnnouncement announcent);
public abstract void AlsoListenTo (string folder_identifier);
public abstract bool IsConnected { get; }
public abstract bool IsConnecting { get; }
protected abstract void Announce (SparkleAnnouncement announcent);
protected abstract void AlsoListenTo (string folder_identifier);
protected List<string> channels = new List<string> ();
protected Dictionary<string,List<SparkleAnnouncement>> recent_announcements = new Dictionary<string, List<SparkleAnnouncement>> ();
protected int max_recent_announcements = 10;
protected Dictionary<string, SparkleAnnouncement> queue_up = new Dictionary<string, SparkleAnnouncement> ();
protected Dictionary<string,SparkleAnnouncement> queue_down = new Dictionary<string, SparkleAnnouncement> ();
protected bool is_connecting;
protected Uri server;
protected Timer reconnect_timer = new Timer { Interval = 60 * 1000, Enabled = true };
private int max_recent_announcements = 10;
private Dictionary<string, List<SparkleAnnouncement>> recent_announcements =
new Dictionary<string, List<SparkleAnnouncement>> ();
private Dictionary<string, SparkleAnnouncement> queue_up =
new Dictionary<string, SparkleAnnouncement> ();
private Dictionary<string, SparkleAnnouncement> queue_down =
new Dictionary<string, SparkleAnnouncement> ();
private Timer reconnect_timer = new Timer {
Interval = 60 * 1000,
Enabled = true
};
public SparkleListenerBase (Uri server, string folder_identifier)
{
this.server = server;
Server = server;
this.channels.Add (folder_identifier);
this.reconnect_timer.Elapsed += delegate {
if (!IsConnected && !this.is_connecting)
if (!IsConnected && !IsConnecting)
Reconnect ();
};
@ -133,29 +145,46 @@ namespace SparkleLib {
public void AnnounceBase (SparkleAnnouncement announcement)
{
if (!this.IsRecentAnnounement (announcement)) {
if (!IsRecentAnnouncement (announcement)) {
if (IsConnected) {
SparkleHelpers.DebugInfo ("Listener",
"Announcing message " + announcement.Message + " to " + announcement.FolderIdentifier + " on " + this.server);
"Announcing message " + announcement.Message + " to " +
announcement.FolderIdentifier + " on " + Server);
Announce (announcement);
this.AddRecentAnnouncement (announcement);
AddRecentAnnouncement (announcement);
} else {
SparkleHelpers.DebugInfo ("Listener", "Can't send message to " + this.server + ". Queuing message");
SparkleHelpers.DebugInfo ("Listener",
"Can't send message to " +
Server + ". Queuing message");
this.queue_up [announcement.FolderIdentifier] = announcement;
}
} else {
SparkleHelpers.DebugInfo ("Listener",
"Already processed message " + announcement.Message + " to " + announcement.FolderIdentifier + " from " + this.server);
"Already processed message " + announcement.Message + " to " +
announcement.FolderIdentifier + " from " + Server);
}
}
public void AlsoListenToBase (string channel)
{
if (!this.channels.Contains (channel) && IsConnected) {
SparkleHelpers.DebugInfo ("Listener",
"Subscribing to channel " + channel);
this.channels.Add (channel);
AlsoListenTo (channel);
}
}
public void Reconnect ()
{
SparkleHelpers.DebugInfo ("Listener", "Trying to reconnect to " + this.server);
SparkleHelpers.DebugInfo ("Listener", "Trying to reconnect to " + Server);
Connect ();
}
@ -168,7 +197,8 @@ namespace SparkleLib {
Connected ();
if (this.queue_up.Count > 0) {
SparkleHelpers.DebugInfo ("Listener", "Delivering " + this.queue_up.Count + " queued messages...");
SparkleHelpers.DebugInfo ("Listener",
"Delivering " + this.queue_up.Count + " queued messages...");
foreach (KeyValuePair<string, SparkleAnnouncement> item in this.queue_up) {
SparkleAnnouncement announcement = item.Value;
@ -192,34 +222,46 @@ namespace SparkleLib {
public void OnAnnouncement (SparkleAnnouncement announcement)
{
SparkleHelpers.DebugInfo ("Listener",
"Got message " + announcement.Message + " from " + announcement.FolderIdentifier + " on " + this.server);
"Got message " + announcement.Message + " from " +
announcement.FolderIdentifier + " on " + Server);
if (IsRecentAnnounement(announcement) ){
if (IsRecentAnnouncement (announcement)) {
SparkleHelpers.DebugInfo ("Listener",
"Ignoring previously processed message " + announcement.Message +
" from " + announcement.FolderIdentifier + " on " + this.server);
" from " + announcement.FolderIdentifier + " on " + Server);
return;
}
SparkleHelpers.DebugInfo ("Listener",
"Processing message " + announcement.Message + " from " + announcement.FolderIdentifier + " on " + this.server);
"Processing message " + announcement.Message + " from " +
announcement.FolderIdentifier + " on " + Server);
AddRecentAnnouncement (announcement);
this.queue_down [announcement.FolderIdentifier] = announcement;
if (Announcement != null)
Announcement (announcement);
if (Received != null)
Received (announcement);
}
private bool IsRecentAnnounement (SparkleAnnouncement announcement)
public virtual void Dispose ()
{
if (!HasRecentAnnouncements (announcement.FolderIdentifier)) {
this.reconnect_timer.Dispose ();
}
private bool IsRecentAnnouncement (SparkleAnnouncement announcement)
{
if (!this.recent_announcements
.ContainsKey (announcement.FolderIdentifier)) {
return false;
} else {
foreach (SparkleAnnouncement recent_announcement in GetRecentAnnouncements (announcement.FolderIdentifier)) {
foreach (SparkleAnnouncement recent_announcement in
GetRecentAnnouncements (announcement.FolderIdentifier)) {
if (recent_announcement.Message.Equals (announcement.Message))
return true;
}
@ -240,39 +282,15 @@ namespace SparkleLib {
private void AddRecentAnnouncement (SparkleAnnouncement announcement)
{
List<SparkleAnnouncement> recent_announcements = this.GetRecentAnnouncements (announcement.FolderIdentifier);
List<SparkleAnnouncement> recent_announcements =
GetRecentAnnouncements (announcement.FolderIdentifier);
if (!IsRecentAnnounement (announcement))
if (!IsRecentAnnouncement (announcement))
recent_announcements.Add (announcement);
if (recent_announcements.Count > this.max_recent_announcements)
recent_announcements.RemoveRange (0, (recent_announcements.Count - this.max_recent_announcements));
}
private bool HasRecentAnnouncements (string folder_identifier)
{
return this.recent_announcements.ContainsKey (folder_identifier);
}
public virtual void Dispose ()
{
this.reconnect_timer.Dispose ();
}
public Uri Server {
get {
return this.server;
}
}
public bool IsConnecting {
get {
return this.is_connecting;
}
recent_announcements.RemoveRange (0,
(recent_announcements.Count - this.max_recent_announcements));
}
}
}

View file

@ -28,30 +28,31 @@ namespace SparkleLib {
public class SparkleListenerTcp : SparkleListenerBase {
private Thread thread;
// these are shared
private readonly Object mutex = new Object();
private Socket socket;
private bool connected;
private Object socket_lock = new Object ();
private Thread thread;
private bool is_connected = false;
private bool is_connecting = false;
public SparkleListenerTcp (Uri server, string folder_identifier) :
base (server, folder_identifier)
{
base.channels.Add (folder_identifier);
this.connected = false;
}
public override bool IsConnected {
get {
bool result = false;
lock (this.socket_lock)
return this.is_connected;
}
}
lock (this.mutex) {
result = this.connected;
}
return result;
public override bool IsConnecting {
get {
lock (this.socket_lock)
return this.is_connecting;
}
}
@ -61,21 +62,29 @@ namespace SparkleLib {
{
SparkleHelpers.DebugInfo ("ListenerTcp", "Connecting to " + Server.Host);
base.is_connecting = true;
this.is_connecting = true;
this.thread = new Thread (
new ThreadStart (delegate {
try {
// Connect and subscribe to the channel
int port = Server.Port;
if (port < 0) port = 9999;
lock (this.mutex) {
this.socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
if (port < 0)
port = 9999;
lock (this.socket_lock) {
this.socket = new Socket (AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp) {
ReceiveTimeout = 30 * 1000
};
this.socket.Connect (Server.Host, port);
base.is_connecting = false;
this.connected = true;
this.is_connecting = false;
this.is_connected = true;
OnConnected ();
@ -85,6 +94,7 @@ namespace SparkleLib {
}
}
byte [] bytes = new byte [4096];
// List to the channels, this blocks the thread
@ -93,20 +103,26 @@ namespace SparkleLib {
if (bytes_read > 0) {
string received = Encoding.UTF8.GetString (bytes);
string line = received.Substring (0, received.IndexOf ("\n"));
string line = received.Substring (0, received.IndexOf ("\n"));
if (!line.Contains ("!"))
continue;
string folder_identifier = line.Substring (0, line.IndexOf ("!"));
string message = this.CleanMessage (line.Substring (line.IndexOf ("!") + 1));
if (!folder_identifier.Equals("debug") &&
!String.IsNullOrEmpty(message))
string message = CleanMessage (line.Substring (line.IndexOf ("!") + 1));
if (!folder_identifier.Equals ("debug") &&
!String.IsNullOrEmpty (message)) {
OnAnnouncement (new SparkleAnnouncement (folder_identifier, message));
}
} else {
SparkleHelpers.DebugInfo ("ListenerTcp", "Error on socket");
lock (this.mutex) {
lock (this.socket_lock) {
this.socket.Close ();
this.connected = false;
this.is_connected = false;
OnDisconnected ();
}
@ -117,6 +133,8 @@ namespace SparkleLib {
} catch (SocketException e) {
SparkleHelpers.DebugInfo ("ListenerTcp", "Could not connect to " + Server + ": " + e.Message);
this.is_connected = false;
this.is_connecting = false;
OnDisconnected ();
}
@ -127,46 +145,44 @@ namespace SparkleLib {
}
public override void AlsoListenTo (string folder_identifier)
protected override void AlsoListenTo (string folder_identifier)
{
string channel = folder_identifier;
string to_send = "subscribe " + folder_identifier + "\n";
if (!base.channels.Contains (channel)) {
base.channels.Add (channel);
if (IsConnected) {
SparkleHelpers.DebugInfo ("ListenerTcp", "Subscribing to channel " + channel);
string to_send = "subscribe " + folder_identifier + "\n";
try {
lock (this.mutex) {
this.socket.Send (Encoding.UTF8.GetBytes (to_send));
}
} catch (SocketException e) {
SparkleHelpers.DebugInfo ("ListenerTcp", "Could not connect to " + Server + ": " + e.Message);
OnDisconnected ();
}
try {
lock (this.socket_lock) {
this.socket.Send (Encoding.UTF8.GetBytes (to_send));
}
} catch (SocketException e) {
SparkleHelpers.DebugInfo ("ListenerTcp",
"Could not connect to " + Server + ": " + e.Message);
this.is_connected = false;
this.is_connecting = false;
OnDisconnected ();
}
}
public override void Announce (SparkleAnnouncement announcement)
protected override void Announce (SparkleAnnouncement announcement)
{
string to_send = "announce " + announcement.FolderIdentifier
+ " " + announcement.Message + "\n";
try {
lock (this.mutex) {
lock (this.socket_lock)
this.socket.Send (Encoding.UTF8.GetBytes (to_send));
}
} catch (SocketException e) {
SparkleHelpers.DebugInfo ("ListenerTcp", "Could not connect to " + Server + ": " + e.Message);
OnDisconnected ();
} catch (SocketException e) {
SparkleHelpers.DebugInfo ("ListenerTcp",
"Could not connect to " + Server + ": " + e.Message);
this.is_connected = false;
this.is_connecting = false;
OnDisconnected ();
}
}
@ -175,12 +191,16 @@ namespace SparkleLib {
{
this.thread.Abort ();
this.thread.Join ();
base.Dispose ();
}
private string CleanMessage(string message)
private string CleanMessage (string message)
{
return message.Trim ().Replace ("\n", "").Replace ("\0", "");
return message.Trim ()
.Replace ("\n", "")
.Replace ("\0", "");
}
}
}

View file

@ -57,9 +57,9 @@ namespace SparkleLib {
protected bool is_buffering = false;
protected bool server_online = true;
public readonly SparkleBackend Backend;
public readonly string LocalPath;
public readonly string Name;
public readonly Uri Url;
public abstract bool AnyDifferences { get; }
public abstract string Identifier { get; }
@ -92,11 +92,12 @@ namespace SparkleLib {
public event ChangesDetectedEventHandler ChangesDetected;
public SparkleRepoBase (string path, SparkleBackend backend)
public SparkleRepoBase (string path)
{
LocalPath = path;
Name = Path.GetFileName (LocalPath);
Backend = backend;
LocalPath = path;
Name = Path.GetFileName (LocalPath);
Url = new Uri (SparkleConfig.DefaultConfig.GetUrlForFolder (Name));
this.poll_interval = this.short_interval;
SyncStatusChanged += delegate (SyncStatus status) {
@ -298,7 +299,7 @@ namespace SparkleLib {
};
// Fetch changes when there is a message in the irc channel
this.listener.Announcement += delegate (SparkleAnnouncement announcement) {
this.listener.Received += delegate (SparkleAnnouncement announcement) {
string identifier = Identifier;
if (announcement.FolderIdentifier.Equals (identifier) &&

View file

@ -1,4 +1,5 @@
EXTRA_DIST = \
config \
AppDelegate.cs \
Growl.framework \
Growl.plist \

View file

@ -46,6 +46,8 @@ namespace SparkleShare {
private NSTextField size_label_value;
private NSTextField history_label;
private NSTextField history_label_value;
private NSButton hidden_close_button;
public SparkleEventLog (IntPtr handle) : base (handle) { }
@ -68,6 +70,19 @@ namespace SparkleShare {
BackingType = NSBackingStore.Buffered;
this.hidden_close_button = new NSButton () {
Frame = new RectangleF (0, 0, 0, 0),
KeyEquivalentModifierMask = NSEventModifierMask.CommandKeyMask,
KeyEquivalent = "w"
};
this.hidden_close_button.Activated += delegate {
PerformClose (this);
};
ContentView.AddSubview (this.hidden_close_button);
this.size_label = new NSTextField () {
Alignment = NSTextAlignment.Right,
BackgroundColor = NSColor.WindowBackground,
@ -237,7 +252,7 @@ namespace SparkleShare {
public class SparkleEventsDelegate : NSWindowDelegate {
public override bool WindowShouldClose (NSObject sender)
{
(sender as SparkleEventLog).OrderOut (this);

View file

@ -462,10 +462,9 @@ namespace SparkleShare {
case PageType.Finished: {
Header = "Project succesfully added!";
Description = "Now you can access the files from " +
"" + Controller.SyncingFolder + " in " +
"your SparkleShare folder.";
Header = "Project " + Path.GetFileName (Controller.PreviousPath) +
" succesfully added!";
Description = "Access the files from your SparkleShare folder.";
if (warnings != null) {
WarningImage = NSImage.ImageNamed ("NSCaution");
@ -505,7 +504,7 @@ namespace SparkleShare {
};
OpenFolderButton.Activated += delegate {
Program.Controller.OpenSparkleShareFolder (Controller.SyncingFolder);
Program.Controller.OpenSparkleShareFolder (Path.GetFileName (Controller.PreviousPath));
};
Buttons.Add (FinishButton);

View file

@ -87,7 +87,9 @@ namespace SparkleShare {
MakeKeyAndOrderFront (this);
OrderFrontRegardless ();
Program.UI.UpdateDockIconVisibility ();
if (Program.UI != null)
Program.UI.UpdateDockIconVisibility ();
}
@ -146,7 +148,9 @@ namespace SparkleShare {
{
base.OrderOut (this);
NSApplication.SharedApplication.RemoveWindowsItem (this);
Program.UI.UpdateDockIconVisibility ();
if (Program.UI != null)
Program.UI.UpdateDockIconVisibility ();
return;
}

View file

@ -11,7 +11,7 @@
<RootNamespace>SparkleShare</RootNamespace>
<AssemblyName>SparkleShare</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<ReleaseVersion>0.8.0</ReleaseVersion>
<ReleaseVersion>0.8.1</ReleaseVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>

View file

@ -16,6 +16,6 @@ Global
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = SparkleShare.csproj
version = 0.8.0
version = 0.8.1
EndGlobalSection
EndGlobal

View file

@ -91,6 +91,15 @@ namespace SparkleShare {
Menu.Delegate = new SparkleStatusIconMenuDelegate ();
}
Controller.UpdateQuitItemEvent += delegate (bool quit_item_enabled) {
InvokeOnMainThread (delegate {
if (QuitMenuItem != null) {
QuitMenuItem.Enabled = quit_item_enabled;
StatusItem.Menu.Update ();
}
});
};
Controller.UpdateMenuEvent += delegate (IconState state) {
InvokeOnMainThread (delegate {
using (var a = new NSAutoreleasePool ()) {
@ -137,6 +146,8 @@ namespace SparkleShare {
break;
}
StatusItem.Menu.Update ();
}
});
};
@ -310,8 +321,8 @@ namespace SparkleShare {
QuitMenuItem = new NSMenuItem () {
Title = "Quit",
Enabled = true
Title = "Quit",
Enabled = Controller.QuitItemEnabled
};
QuitMenuItem.Activated += delegate {

View file

@ -69,6 +69,8 @@ namespace SparkleShare {
if (Program.Controller.FirstRun) {
Setup = new SparkleSetup ();
Setup.Controller.ShowSetupPage ();
UpdateDockIconVisibility ();
}
}
}

View file

@ -29,7 +29,7 @@ namespace SparkleShare {
public SparkleBubblesController ()
{
Program.Controller.ConflictNotificationRaised += delegate {
ShowBubble ("Ouch! Mid-air collision!",
ShowBubble ("Conflict detected.",
"Don't worry, SparkleShare made a copy of each conflicting file.",
null);
};

View file

@ -262,8 +262,7 @@ namespace SparkleShare {
public abstract string EventLogHTML { get; }
public abstract string DayEntryHTML { get; }
public abstract string EventEntryHTML { get; }
public string GetHTMLLog (List<SparkleChangeSet> change_sets)
{
List <ActivityDay> activity_days = new List <ActivityDay> ();
@ -456,6 +455,7 @@ namespace SparkleShare {
.Replace ("<!-- $event-avatar-url -->", change_set_avatar)
.Replace ("<!-- $event-time -->", timestamp)
.Replace ("<!-- $event-folder -->", change_set.Folder)
.Replace ("<!-- $event-url -->", change_set.Url.ToString ())
.Replace ("<!-- $event-revision -->", change_set.Revision)
.Replace ("<!-- $event-folder-color -->", AssignColor (change_set.Folder))
.Replace ("<!-- $event-comments -->", comments);
@ -570,16 +570,23 @@ namespace SparkleShare {
// Adds a repository to the list of repositories
private void AddRepository (string folder_path)
{
if (folder_path.Equals (SparkleConfig.DefaultConfig.TmpPath))
return;
SparkleRepoBase repo = null;
string folder_name = Path.GetFileName (folder_path);
string backend = SparkleConfig.DefaultConfig.GetBackendForFolder (folder_name);
string folder_name = Path.GetFileName (folder_path);
string backend = SparkleConfig.DefaultConfig.GetBackendForFolder (folder_name);
try {
repo = (SparkleRepoBase) Activator.CreateInstance (
Type.GetType ("SparkleLib.SparkleRepo" + backend + ", SparkleLib"),
folder_path
);
} catch {
SparkleHelpers.DebugInfo ("Controller",
"Failed to load \"" + backend + "\" backend for \"" + folder_name + "\"");
if (backend == null)
return;
SparkleRepoBase repo = new SparkleRepoGit (folder_path, SparkleBackend.DefaultBackend);
}
repo.NewChangeSet += delegate (SparkleChangeSet change_set) {
@ -624,6 +631,7 @@ namespace SparkleShare {
UpdateState ();
};
Repositories.Add (repo);
repo.Initialize ();
}
@ -770,6 +778,7 @@ namespace SparkleShare {
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "ssh-add";
process.StartInfo.Arguments = "\"" + key_file_path + "\"";
process.StartInfo.CreateNoWindow = true;
process.Start ();
process.WaitForExit ();
@ -817,7 +826,7 @@ namespace SparkleShare {
string key_file_path = Path.Combine (keys_path, key_file_name);
if (File.Exists (key_file_path)) {
SparkleHelpers.DebugInfo ("Config", "Key already exists ('" + key_file_name + "'), " +
SparkleHelpers.DebugInfo ("Auth", "Key already exists ('" + key_file_name + "'), " +
"leaving it untouched");
return;
}
@ -830,10 +839,11 @@ namespace SparkleShare {
EnableRaisingEvents = true
};
process.StartInfo.WorkingDirectory = keys_path;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WorkingDirectory = keys_path;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.FileName = "ssh-keygen";
process.StartInfo.FileName = "ssh-keygen";
process.StartInfo.CreateNoWindow = true;
// -t is the crypto type
// -P is the password (none)
@ -843,8 +853,8 @@ namespace SparkleShare {
process.Start ();
process.WaitForExit ();
SparkleHelpers.DebugInfo ("Config", "Created private key '" + key_file_name + "'");
SparkleHelpers.DebugInfo ("Config", "Created public key '" + key_file_name + ".pub'");
SparkleHelpers.DebugInfo ("Auth", "Created private key '" + key_file_name + "'");
SparkleHelpers.DebugInfo ("Auth", "Created public key '" + key_file_name + ".pub'");
// Add some restrictions to what the key can
// do when uploaded to the server
@ -877,7 +887,7 @@ namespace SparkleShare {
if (!Directory.Exists (avatar_path)) {
Directory.CreateDirectory (avatar_path);
SparkleHelpers.DebugInfo ("Config", "Created '" + avatar_path + "'");
SparkleHelpers.DebugInfo ("Avatar", "Created '" + avatar_path + "'");
}
foreach (string raw_email in emails) {
@ -923,11 +933,11 @@ namespace SparkleShare {
lock (this.avatar_lock)
File.WriteAllBytes (avatar_file_path, buffer);
SparkleHelpers.DebugInfo ("Controller", "Fetched gravatar for " + email);
SparkleHelpers.DebugInfo ("Avatar", "Fetched gravatar for " + email);
}
} catch (WebException e) {
SparkleHelpers.DebugInfo ("Controller", "Failed fetching gravatar for " + email);
SparkleHelpers.DebugInfo ("Avatar", "Failed fetching gravatar for " + email);
// Stop downloading further avatars if we have no internet access
if (e.Status == WebExceptionStatus.Timeout){
@ -971,7 +981,7 @@ namespace SparkleShare {
public void FetchFolder (string server, string remote_folder)
{
server = server.Trim ();
server = server.Trim ();
remote_folder = remote_folder.Trim ();
string tmp_path = SparkleConfig.DefaultConfig.TmpPath;
@ -980,26 +990,42 @@ namespace SparkleShare {
File.SetAttributes (tmp_path, File.GetAttributes (tmp_path) | FileAttributes.Hidden);
}
// Strip the '.git' from the name
string canonical_name = Path.GetFileNameWithoutExtension (remote_folder);
string tmp_folder = Path.Combine (tmp_path, canonical_name);
string backend = Path.GetExtension (remote_folder);
string backend = null;
if (!string.IsNullOrEmpty (backend)) {
backend = backend.Substring (1);
/* if (remote_folder.EndsWith (".hg")) {
remote_folder = remote_folder.Substring (0, (remote_folder.Length - 3));
fetcher = new SparkleFetcherHg (server, remote_folder, tmp_folder);
backend = "Hg";
char [] letters = backend.ToCharArray ();
letters [0] = char.ToUpper (letters [0]);
backend = new string (letters);
} else if (remote_folder.EndsWith (".scp")) {
remote_folder = remote_folder.Substring (0, (remote_folder.Length - 4));
fetcher = new SparkleFetcherScp (server, remote_folder, tmp_folder);
backend = "Scp";
} else {*/
this.fetcher = new SparkleFetcherGit (server, remote_folder, tmp_folder);
} else {
backend = "Git";
//}
}
try {
this.fetcher = (SparkleFetcherBase) Activator.CreateInstance (
Type.GetType ("SparkleLib.SparkleFetcher" + backend + ", SparkleLib"),
server,
remote_folder,
tmp_folder
);
} catch {
SparkleHelpers.DebugInfo ("Controller",
"Failed to load \"" + backend + "\" backend for \"" + canonical_name + "\"");
if (FolderFetchError != null)
FolderFetchError (Path.Combine (server, remote_folder));
return;
}
bool target_folder_exists = Directory.Exists (
Path.Combine (SparkleConfig.DefaultConfig.FoldersPath, canonical_name));
@ -1046,7 +1072,6 @@ namespace SparkleShare {
}
};
this.fetcher.Failed += delegate {
if (FolderFetchError != null)
FolderFetchError (this.fetcher.RemoteUrl);
@ -1057,7 +1082,6 @@ namespace SparkleShare {
Directory.Delete (tmp_path, true);
};
this.fetcher.ProgressChanged += delegate (double percentage) {
if (FolderFetching != null)
FolderFetching (percentage);

View file

@ -174,8 +174,9 @@ namespace SparkleShare {
TreeIter iter;
this.combo_box.GetActiveIter (out iter);
string selection = (string) this.combo_box.Model.GetValue (iter, 0);
TreePath path = this.combo_box.Model.GetPath (iter);
if (selection.Equals (_("All Folders")))
if (path.Indices [0] == 0)
Controller.SelectedFolder = null;
else
Controller.SelectedFolder = selection;
@ -224,7 +225,7 @@ namespace SparkleShare {
Application.Invoke (delegate {
this.spinner.Stop ();
this.web_view.LoadString (html, null, null, "file:///");
this.web_view.LoadString (html, null, null, "file://");
this.content_wrapper.Remove (this.content_wrapper.Child);
this.content_wrapper.Add (this.scrolled_window);
this.content_wrapper.ShowAll ();

View file

@ -66,8 +66,8 @@ namespace SparkleShare {
// A short delay is less annoying than
// a flashing window
if (watch.ElapsedMilliseconds < 500)
Thread.Sleep (500 - (int) watch.ElapsedMilliseconds);
if (watch.ElapsedMilliseconds < 750)
Thread.Sleep (750 - (int) watch.ElapsedMilliseconds);
if (UpdateContentEvent != null)
UpdateContentEvent (html);

View file

@ -441,20 +441,21 @@ namespace SparkleShare {
UrgencyHint = true;
if (!HasToplevelFocus) {
string title = String.Format (_("{0} has been successfully added"), Controller.SyncingFolder);
string title = _("Project successfully added!");
string subtext = "";
SparkleUI.Bubbles.Controller.ShowBubble (title, subtext, null);
}
Header = _("Project successfully added!");
Header = _("Project " + System.IO.Path.GetFileName (Controller.PreviousPath) +
" successfully added!");
Description = _("Access the files from your SparkleShare folder.");
// A button that opens the synced folder
Button open_folder_button = new Button (_("Open Folder"));
open_folder_button.Clicked += delegate {
Program.Controller.OpenSparkleShareFolder (Controller.SyncingFolder);
Program.Controller.OpenSparkleShareFolder (System.IO.Path.GetFileName (Controller.PreviousPath));
};
Button finish_button = new Button (_("Finish"));

View file

@ -33,11 +33,13 @@ namespace SparkleShare {
public SparkleStatusIconController Controller = new SparkleStatusIconController ();
// TODO: fix case
private Timer Animation;
private Gdk.Pixbuf [] AnimationFrames;
private int FrameNumber;
private string StateText;
private Menu Menu;
private MenuItem quit_item;
#if HAVE_APP_INDICATOR
private ApplicationIndicator indicator;
@ -78,58 +80,70 @@ namespace SparkleShare {
CreateMenu ();
Controller.UpdateQuitItemEvent += delegate (bool quit_item_enabled) {
Application.Invoke (delegate {
if (this.quit_item != null) {
this.quit_item.Sensitive = quit_item_enabled;
Menu.ShowAll ();
}
});
};
Controller.UpdateMenuEvent += delegate (IconState state) {
Application.Invoke (delegate {
switch (state) {
case IconState.Idle:
switch (state) {
case IconState.Idle:
Animation.Stop ();
Animation.Stop ();
if (Controller.Folders.Length == 0)
StateText = _("Welcome to SparkleShare!");
else
StateText = _("Up to date") + Controller.FolderSize;
if (Controller.Folders.Length == 0)
StateText = _("Welcome to SparkleShare!");
else
StateText = _("Up to date") + Controller.FolderSize;
#if HAVE_APP_INDICATOR
this.indicator.IconName = "process-syncing-sparkleshare-i";
#else
this.status_icon.Pixbuf = AnimationFrames [0];
#endif
#if HAVE_APP_INDICATOR
this.indicator.IconName = "process-syncing-sparkleshare-i";
#else
this.status_icon.Pixbuf = AnimationFrames [0];
#endif
UpdateStateText ();
CreateMenu ();
UpdateStateText ();
CreateMenu ();
break;
break;
case IconState.Syncing:
case IconState.Syncing:
StateText = _("Syncing… ") +
Controller.ProgressPercentage + "% " +
Controller.ProgressSpeed;
StateText = _("Syncing… ") +
Controller.ProgressPercentage + "% " +
Controller.ProgressSpeed;
UpdateStateText ();
UpdateStateText ();
if (!Animation.Enabled)
Animation.Start ();
if (!Animation.Enabled)
Animation.Start ();
break;
break;
case IconState.Error:
case IconState.Error:
Animation.Stop ();
Animation.Stop ();
StateText = _("Not everything is synced");
UpdateStateText ();
CreateMenu ();
#if HAVE_APP_INDICATOR
this.indicator.IconName = "sparkleshare-syncing-error";
#else
this.status_icon.Pixbuf = SparkleUIHelpers.GetIcon ("sparkleshare-syncing-error", 24);
#endif
StateText = _("Not everything is synced");
UpdateStateText ();
CreateMenu ();
break;
}
#if HAVE_APP_INDICATOR
this.indicator.IconName = "sparkleshare-syncing-error";
#else
this.status_icon.Pixbuf = SparkleUIHelpers.GetIcon ("sparkleshare-syncing-error", 24);
#endif
break;
}
Menu.ShowAll ();
});
};
}
@ -311,13 +325,15 @@ namespace SparkleShare {
Menu.Add (new SeparatorMenuItem ());
// A menu item that quits the application
MenuItem quit_item = new MenuItem (_("Quit"));
this.quit_item = new MenuItem (_("Quit")) {
Sensitive = Controller.QuitItemEnabled
};
quit_item.Activated += delegate {
this.quit_item.Activated += delegate {
Program.Controller.Quit ();
};
Menu.Add (quit_item);
Menu.Add (this.quit_item);
Menu.ShowAll ();
#if HAVE_APP_INDICATOR

View file

@ -38,6 +38,9 @@ namespace SparkleShare {
public event UpdateMenuEventHandler UpdateMenuEvent;
public delegate void UpdateMenuEventHandler (IconState state);
public event UpdateQuitItemEventHandler UpdateQuitItemEvent;
public delegate void UpdateQuitItemEventHandler (bool quit_item_enabled);
public IconState CurrentState = IconState.Idle;
public string [] Folders {
@ -76,6 +79,14 @@ namespace SparkleShare {
}
}
public bool QuitItemEnabled {
get {
return (CurrentState != IconState.Syncing &&
CurrentState != IconState.SyncingDown &&
CurrentState != IconState.SyncingUp);
}
}
public SparkleStatusIconController ()
{
@ -89,6 +100,9 @@ namespace SparkleShare {
if (CurrentState != IconState.Error)
CurrentState = IconState.Idle;
if (UpdateQuitItemEvent != null)
UpdateQuitItemEvent (QuitItemEnabled);
if (UpdateMenuEvent != null)
UpdateMenuEvent (CurrentState);
};
@ -97,6 +111,9 @@ namespace SparkleShare {
Program.Controller.OnSyncing += delegate {
CurrentState = IconState.Syncing;
if (UpdateQuitItemEvent != null)
UpdateQuitItemEvent (QuitItemEnabled);
if (UpdateMenuEvent != null)
UpdateMenuEvent (IconState.Syncing);
};
@ -105,6 +122,9 @@ namespace SparkleShare {
Program.Controller.OnError += delegate {
CurrentState = IconState.Error;
if (UpdateQuitItemEvent != null)
UpdateQuitItemEvent (QuitItemEnabled);
if (UpdateMenuEvent != null)
UpdateMenuEvent (IconState.Error);
};

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
if [[ $UID -eq 0 ]]; then
echo "Cannot run as root. Things would go utterly wrong."

View file

@ -1,13 +1,13 @@
dnl Process this file with autoconf to produce a configure script.
m4_define([sparkleshare_version],
[0.8.0])
[0.8.1])
m4_define([sparkleshare_asm_version],
[0.8.0])
[0.8.1])
AC_PREREQ([2.54])
AC_INIT([SparkleShare], sparkleshare_version)
AM_INIT_AUTOMAKE([1.11 dist-bzip2 dist-zip foreign tar-ustar])
AM_INIT_AUTOMAKE([1.11 dist-bzip2 dist-zip foreign tar-pax])
AM_MAINTAINER_MODE
dnl Export Version Info

View file

@ -1,6 +1,6 @@
<div class='event-entry' style='background-image: url("<!-- $event-avatar-url -->");'>
<div class='event-user-name'><!-- $event-user-name --></div>
<div class='event-folder'><!-- $event-folder --></div>
<div class='event-folder'><!-- $event-folder --><span><!-- $event-url --></span></div>
<!-- $event-entry-content -->

View file

@ -88,23 +88,23 @@
<style>
* {
box-sizing: border-box;
box-sizing: border-box;
-webkit-box-sizing: border-box;
}
body {
background-color: #fff;
color: <!-- $body-color -->;
font-size: <!-- $body-font-size -->;
font-family: <!-- $body-font-family -->;
margin: 0;
padding: 18px;
background-color: #fafafa;
color: <!-- $body-color -->;
font-size: <!-- $body-font-size -->;
font-family: <!-- $body-font-family -->;
margin: 0;
padding: 18px;
-webkit-user-select: none;
}
small {
font-size: <!-- $small-font-size -->;
color: <!-- $secondary-font-color -->;
color: <!-- $secondary-font-color -->;
}
.moved-arrow {
@ -152,23 +152,45 @@
font-weight: bold;
}
.event-folder {
float: right;
color: <!-- $secondary-font-color -->;
position: relative;
cursor: pointer;
}
.event-folder span {
display: none
}
.event-folder:hover span {
display: block;
position: absolute;
top: 20px;
right: 0;
background-color: rgba(20, 20, 20, 0.8);
color: #fafafa;
padding: 7px 12px;
border-radius: 5px;
font-family: sans-serif;
font-size: 12px;
float:right;
}
.event-entry {
margin-bottom: 24px;
padding-bottom: 24px;
border-bottom: 1px <!-- $secondary-font-color --> solid;
width: 95%;
border-bottom: 1px #ddd solid;
width: 99%;
margin-left: auto;
margin-right: auto;
padding-left: 72px;
padding-right: 12px;
background-repeat: no-repeat;
background-position: 12px top;
display: block;
display: block;
}
.action {
@ -209,16 +231,16 @@
dd {
display: none;
overflow: hidden;
text-overflow: ellipsis;
width: 90%;
padding: 0;
padding-top: 2px;
padding-bottom: 2px;
padding-left: 20px;
margin: 0;
margin-bottom: 1px;
background-repeat: no-repeat;
overflow: hidden;
text-overflow: ellipsis;
width: 90%;
padding: 0;
padding-top: 2px;
padding-bottom: 2px;
padding-left: 20px;
margin: 0;
margin-bottom: 1px;
background-repeat: no-repeat;
background-position: center left;
}
@ -252,17 +274,17 @@
position: relative;
top: 0;
left: 0;
float: left;
margin-right: 12px;
width: 48px;
background-image: url('<!-- $no-buddy-icon-background-image -->');
background-repeat: no-repeat;
float: left;
margin-right: 12px;
width: 48px;
background-image: url('<!-- $no-buddy-icon-background-image -->');
background-repeat: no-repeat;
background-position: top center;
}
.buddy-icon {
width: 48px;
height: 48px;
width: 48px;
height: 48px;
}
.comments {
@ -295,23 +317,23 @@
}
.comment-textarea {
box-sizing: border-box;
box-sizing: border-box;
-webkit-box-sizing: border-box;
width: 100%;
height: 3em;
padding-bottom: 6px;
width: 100%;
height: 3em;
padding-bottom: 6px;
}
.comments-section {
box-sizing: border-box;
box-sizing: border-box;
-webkit-box-sizing: border-box;
width: 100%;
width: 100%;
}
.comments-wrapper {
box-sizing: border-box;
box-sizing: border-box;
-webkit-box-sizing: border-box;
width: 100%;
width: 100%;
margin-top: 12px;
}