Эх сурвалжийг харах

Merge branch 'develop' into refactor_remote_operation_to_read_file

David A. Velasco 11 жил өмнө
parent
commit
35c5a260e2
65 өөрчлөгдсөн 546 нэмэгдсэн , 3984 устгасан
  1. 3 3
      AndroidManifest.xml
  2. 8 6
      SETUP.md
  3. 81 0
      oc_framework/pom.xml
  4. 50 3
      oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedSslSocketFactory.java
  5. 144 0
      oc_framework/src/com/owncloud/android/oc_framework/network/ServerNameIndicator.java
  6. 2 2
      oc_jb_workaround/AndroidManifest.xml
  7. BIN
      oc_jb_workaround/res/drawable-xhdpi/main_app_icon.png
  8. BIN
      oc_jb_workaround/res/drawable-xhdpi/workaround_app_icon.png
  9. 16 6
      pom.xml
  10. BIN
      res/drawable-hdpi/winter_holidays_icon.png
  11. BIN
      res/drawable-ldpi/winter_holidays_icon.png
  12. BIN
      res/drawable-mdpi/winter_holidays_icon.png
  13. 30 24
      res/layout-land/account_setup.xml
  14. 2 0
      res/values-az/strings.xml
  15. 2 0
      res/values-ca/strings.xml
  16. 7 7
      res/values-cs-rCZ/strings.xml
  17. 4 4
      res/values-de/strings.xml
  18. 1 0
      res/values-es/strings.xml
  19. 18 0
      res/values-eu/strings.xml
  20. 76 0
      res/values-id/strings.xml
  21. 2 0
      res/values-ja-rJP/strings.xml
  22. 2 0
      res/values-nl/strings.xml
  23. 16 0
      res/values-pt-rPT/strings.xml
  24. 5 0
      res/values-sk-rSK/strings.xml
  25. 1 1
      res/values-tr/strings.xml
  26. 1 1
      res/values/strings.xml
  27. 3 2
      src/com/owncloud/android/files/managers/OCNotificationManager.java
  28. 4 3
      src/com/owncloud/android/files/services/FileDownloader.java
  29. 8 4
      src/com/owncloud/android/files/services/FileUploader.java
  30. 1 1
      src/com/owncloud/android/operations/ChunkedUploadFileOperation.java
  31. 5 4
      src/com/owncloud/android/syncadapter/FileSyncAdapter.java
  32. 5 4
      src/com/owncloud/android/ui/activity/AccountSelectActivity.java
  33. 5 1
      src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java
  34. 8 17
      src/com/owncloud/android/ui/activity/FileDisplayActivity.java
  35. 5 0
      src/com/owncloud/android/ui/activity/GenericExplanationActivity.java
  36. 2 0
      src/com/owncloud/android/ui/activity/LogHistoryActivity.java
  37. 4 1
      src/com/owncloud/android/ui/activity/PinCodeActivity.java
  38. 2 8
      src/com/owncloud/android/ui/activity/Preferences.java
  39. 2 0
      src/com/owncloud/android/ui/activity/UploadFilesActivity.java
  40. 2 1
      src/com/owncloud/android/ui/dialog/ChangelogDialog.java
  41. 7 6
      src/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java
  42. 2 0
      src/com/owncloud/android/ui/preview/PreviewImageActivity.java
  43. 10 1
      src/com/owncloud/android/utils/DisplayUtils.java
  44. 0 5
      third_party/transifex-client/.gitignore
  45. 0 21
      third_party/transifex-client/DEVELOPMENT.rst
  46. 0 343
      third_party/transifex-client/LICENSE
  47. 0 6
      third_party/transifex-client/MANIFEST.in
  48. 0 30
      third_party/transifex-client/README.rst
  49. 0 56
      third_party/transifex-client/setup.py
  50. 0 0
      third_party/transifex-client/tests/__init__.py
  51. 0 65
      third_party/transifex-client/tests/test_processors.py
  52. 0 531
      third_party/transifex-client/tests/test_project.py
  53. 0 109
      third_party/transifex-client/tx
  54. 0 19
      third_party/transifex-client/txclib/__init__.py
  55. 0 576
      third_party/transifex-client/txclib/commands.py
  56. 0 115
      third_party/transifex-client/txclib/config.py
  57. 0 13
      third_party/transifex-client/txclib/exceptions.py
  58. 0 46
      third_party/transifex-client/txclib/http_utils.py
  59. 0 37
      third_party/transifex-client/txclib/log.py
  60. 0 241
      third_party/transifex-client/txclib/parsers.py
  61. 0 54
      third_party/transifex-client/txclib/processors.py
  62. 0 1233
      third_party/transifex-client/txclib/project.py
  63. 0 21
      third_party/transifex-client/txclib/urls.py
  64. 0 259
      third_party/transifex-client/txclib/utils.py
  65. 0 94
      third_party/transifex-client/txclib/web.py

+ 3 - 3
AndroidManifest.xml

@@ -18,8 +18,8 @@
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
  -->
  -->
 <manifest package="com.owncloud.android"
 <manifest package="com.owncloud.android"
-    android:versionCode="105000"
-    android:versionName="1.5.0" xmlns:android="http://schemas.android.com/apk/res/android">
+    android:versionCode="105002"
+    android:versionName="1.5.2" xmlns:android="http://schemas.android.com/apk/res/android">
 
 
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
@@ -113,7 +113,7 @@
         <service
         <service
             android:name=".syncadapter.FileSyncService"
             android:name=".syncadapter.FileSyncService"
             android:exported="true" 
             android:exported="true" 
-            android:process=":sync">
+            >
             <intent-filter>
             <intent-filter>
                 <action android:name="android.content.SyncAdapter" />
                 <action android:name="android.content.SyncAdapter" />
             </intent-filter>
             </intent-filter>

+ 8 - 6
SETUP.md

@@ -25,15 +25,17 @@ NOTE: You must have the Android SDK 'tools/', and 'platforms-tools/' folders in
 
 
 ### 3. Building with console/maven:
 ### 3. Building with console/maven:
 
 
-NOTE: You must have mvn in your environment path
+NOTE: You must have mvn (version >= 3.1.1) in your environment path. Current Android 'platforms-tools' need to be installed.
 
 
 * Download/install Android plugin for Maven, then build ownCloud with mvn:
 * Download/install Android plugin for Maven, then build ownCloud with mvn:
 * "cd .."
 * "cd .."
 * "git clone https://github.com/mosabua/maven-android-sdk-deployer.git"
 * "git clone https://github.com/mosabua/maven-android-sdk-deployer.git"
 * "cd maven-android-sdk-deployer"
 * "cd maven-android-sdk-deployer"
-* "mvn -pl com.simpligility.android.sdk-deployer:android-17 -am install"
-* "cd ../android"
-* Now you can create APK using "mvn package"
+* "mvn -pl com.simpligility.android.sdk-deployer:android-19 -am install"
+* "cd ../android/oc_framework"
+* "mvn install"
+* "cd .."
+* Now you can create ownCloud APK using "mvn package"
 
 
 ### 4. Building with Eclipse:
 ### 4. Building with Eclipse:
 
 
@@ -44,13 +46,13 @@ NOTE: You must have the Android SDK 'tools/', and 'platforms-tools/' folders in
 * Clean project and compile.
 * Clean project and compile.
 * If any error appear, check the project properties; in the 'Android' section, API Level should be greater or equal than 14.
 * If any error appear, check the project properties; in the 'Android' section, API Level should be greater or equal than 14.
 * Make sure android/actionbarsherlock/library/bin/library.jar was created.
 * Make sure android/actionbarsherlock/library/bin/library.jar was created.
-* Create a new "Android Project from Existing Code". Choose android/oc_framework/library as root.
+* Create a new "Android Project from Existing Code". Choose android/oc_framework as root.
 * Clean project and compile.
 * Clean project and compile.
 * If any error appear, check the project properties; in the 'Android' section, API Level should be 19 or greater.
 * If any error appear, check the project properties; in the 'Android' section, API Level should be 19 or greater.
 * Make sure android/oc_framework/bin/classes.jar was created.  
 * Make sure android/oc_framework/bin/classes.jar was created.  
 * Import ownCloud Android project.
 * Import ownCloud Android project.
 * Clean project and compile.
 * Clean project and compile.
-* If any error appears, check the project properties; in the 'Android' section:
+* If any error appears, check the project properties of owncloud-android project; in the 'Android' section:
   - API Level should be 19 or greater.
   - API Level should be 19 or greater.
   - Two library projects should appear referred in the bottom square: actionbarsherlock/library and oc_framework. Add them if needed. 
   - Two library projects should appear referred in the bottom square: actionbarsherlock/library and oc_framework. Add them if needed. 
 * After those actions you should be good to go. HAVE FUN!
 * After those actions you should be good to go. HAVE FUN!

+ 81 - 0
oc_framework/pom.xml

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.owncloud.android</groupId>
+    <artifactId>oc_framework</artifactId>
+    <version>${owncloud.version}</version>
+    <packaging>jar</packaging>
+    <name>oc_framework for Owncloud Android</name>
+
+    <properties>
+        <owncloud.version>1.5.1-SNAPSHOT</owncloud.version>
+        <java-version>1.6</java-version>
+        <!-- Given by maven-android-sdk-deployer -->
+        <google.android-version>4.4_r1</google.android-version>
+        <!-- Usually the latest Android API -->
+        <google.android-api>19</google.android-api>
+    </properties>
+
+    <description>oc_framwork for Owncloud for Android</description>
+    
+    <dependencies>
+
+        <dependency>
+            <groupId>android</groupId>
+            <artifactId>android</artifactId>
+            <version>${google.android-version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+          <groupId>commons-httpclient</groupId>
+          <artifactId>commons-httpclient</artifactId>
+          <version>3.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>jackrabbit-webdav</artifactId>
+            <version>2.5.2</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+
+        <sourceDirectory>src</sourceDirectory>
+
+        <plugins>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.0</version>
+                <configuration>
+                    <source>${java-version}</source>
+                    <target>${java-version}</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+                <artifactId>android-maven-plugin</artifactId>
+                <version>3.8.0</version>
+                <configuration>
+                    <sdk>
+                        <path>${env.ANDROID_HOME}</path>
+                        <platform>${google.android-api}</platform>
+                    </sdk>
+                </configuration>
+                <extensions>true</extensions>
+            </plugin>
+
+        </plugins>
+
+    </build>
+
+</project>
+

+ 50 - 3
oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedSslSocketFactory.java

@@ -24,12 +24,15 @@ import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.Socket;
 import java.net.SocketAddress;
 import java.net.SocketAddress;
 import java.net.UnknownHostException;
 import java.net.UnknownHostException;
+//import java.security.Provider;
 import java.security.cert.X509Certificate;
 import java.security.cert.X509Certificate;
+//import java.util.Enumeration;
 
 
 import javax.net.SocketFactory;
 import javax.net.SocketFactory;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLHandshakeException;
+//import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocket;
@@ -39,6 +42,7 @@ import org.apache.commons.httpclient.params.HttpConnectionParams;
 import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
 import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
 import org.apache.http.conn.ssl.X509HostnameVerifier;
 import org.apache.http.conn.ssl.X509HostnameVerifier;
 
 
+//import android.os.Build;
 import android.util.Log;
 import android.util.Log;
 
 
 
 
@@ -84,8 +88,47 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
         return socket;
         return socket;
     }
     }
 
 
+    /*
+    private void logSslInfo() {
+    	if (Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) {
+	    	Log.v(TAG, "SUPPORTED SSL PARAMETERS");
+	    	logSslParameters(mSslContext.getSupportedSSLParameters());
+	    	Log.v(TAG, "DEFAULT SSL PARAMETERS");
+	    	logSslParameters(mSslContext.getDefaultSSLParameters());
+	    	Log.i(TAG, "CURRENT PARAMETERS");
+	    	Log.i(TAG, "Protocol: " + mSslContext.getProtocol());
+    	}
+    	Log.i(TAG, "PROVIDER");
+    	logSecurityProvider(mSslContext.getProvider());
+	}
     
     
-    /**
+    private void logSecurityProvider(Provider provider) {
+    	Log.i(TAG, "name: " + provider.getName());
+    	Log.i(TAG, "version: " + provider.getVersion());
+    	Log.i(TAG, "info: " + provider.getInfo());
+    	Enumeration<?> keys = provider.propertyNames();
+    	String key;
+    	while (keys.hasMoreElements()) {
+    		key = (String) keys.nextElement();
+        	Log.i(TAG, "  property " + key + " : " + provider.getProperty(key));
+    	}
+	}
+
+	private void logSslParameters(SSLParameters params) {
+    	Log.v(TAG, "Cipher suites: ");
+    	String [] elements = params.getCipherSuites();
+    	for (int i=0; i<elements.length ; i++) {
+    		Log.v(TAG, "  " + elements[i]);
+    	}
+    	Log.v(TAG, "Protocols: ");
+    	elements = params.getProtocols();
+    	for (int i=0; i<elements.length ; i++) {
+    		Log.v(TAG, "  " + elements[i]);
+    	}
+	}
+	*/
+
+	/**
      * Attempts to get a new socket connection to the given host within the
      * Attempts to get a new socket connection to the given host within the
      * given time limit.
      * given time limit.
      * 
      * 
@@ -110,6 +153,9 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
             throw new IllegalArgumentException("Parameters may not be null");
             throw new IllegalArgumentException("Parameters may not be null");
         } 
         } 
         int timeout = params.getConnectionTimeout();
         int timeout = params.getConnectionTimeout();
+        
+        //logSslInfo();
+        
         SocketFactory socketfactory = mSslContext.getSocketFactory();
         SocketFactory socketfactory = mSslContext.getSocketFactory();
         Log.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
         Log.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
         Socket socket = socketfactory.createSocket();
         Socket socket = socketfactory.createSocket();
@@ -117,12 +163,13 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
         SocketAddress remoteaddr = new InetSocketAddress(host, port);
         SocketAddress remoteaddr = new InetSocketAddress(host, port);
         socket.setSoTimeout(params.getSoTimeout());
         socket.setSoTimeout(params.getSoTimeout());
         socket.bind(localaddr);
         socket.bind(localaddr);
+        ServerNameIndicator.setServerNameIndication(host, (SSLSocket)socket);
         socket.connect(remoteaddr, timeout);
         socket.connect(remoteaddr, timeout);
         verifyPeerIdentity(host, port, socket);
         verifyPeerIdentity(host, port, socket);
         return socket;
         return socket;
     }
     }
 
 
-    /**
+	/**
      * @see ProtocolSocketFactory#createSocket(java.lang.String,int)
      * @see ProtocolSocketFactory#createSocket(java.lang.String,int)
      */
      */
     public Socket createSocket(String host, int port) throws IOException,
     public Socket createSocket(String host, int port) throws IOException,
@@ -238,5 +285,5 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
             throw io;
             throw io;
         }
         }
     }
     }
-
+    
 }
 }

+ 144 - 0
oc_framework/src/com/owncloud/android/oc_framework/network/ServerNameIndicator.java

@@ -0,0 +1,144 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   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/>.
+ *
+ */
+
+package com.owncloud.android.oc_framework.network;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.net.ssl.SSLSocket;
+
+import android.util.Log;
+
+
+/**
+ * Enables the support of Server Name Indication if existing 
+ * in the underlying network implementation.
+ * 
+ * Build as a singleton.
+ * 
+ * @author David A. Velasco
+ */
+public class ServerNameIndicator {
+	
+	private static final String TAG = ServerNameIndicator.class.getSimpleName();
+	
+	private static final AtomicReference<ServerNameIndicator> mSingleInstance = new AtomicReference<ServerNameIndicator>();
+	
+	private static final String METHOD_NAME = "setHostname";
+	
+	private final WeakReference<Class<?>> mSSLSocketClassRef;
+	private final WeakReference<Method> mSetHostnameMethodRef;
+	
+	
+	/**
+	 * Private constructor, class is a singleton.
+	 * 
+	 * @param sslSocketClass		Underlying implementation class of {@link SSLSocket} used to connect with the server. 
+	 * @param setHostnameMethod		Name of the method to call to enable the SNI support.
+	 */
+	private ServerNameIndicator(Class<?> sslSocketClass, Method setHostnameMethod) {
+		mSSLSocketClassRef = new WeakReference<Class<?>>(sslSocketClass);
+		mSetHostnameMethodRef = (setHostnameMethod == null) ? null : new WeakReference<Method>(setHostnameMethod);
+	}
+	
+	
+	/**
+	 * Calls the {@code #setHostname(String)} method of the underlying implementation 
+	 * of {@link SSLSocket} if exists.
+	 * 
+	 * Creates and initializes the single instance of the class when needed
+	 *
+	 * @param hostname 		The name of the server host of interest.
+	 * @param sslSocket 	Client socket to connect with the server.
+	 */
+	public static void setServerNameIndication(String hostname, SSLSocket sslSocket) {
+		final Method setHostnameMethod = getMethod(sslSocket);
+		if (setHostnameMethod != null) {
+			try {
+				setHostnameMethod.invoke(sslSocket, hostname);
+				Log.i(TAG, "SNI done, hostname: " + hostname);
+				
+			} catch (IllegalArgumentException e) {
+				Log.e(TAG, "Call to SSLSocket#setHost(String) failed ", e);
+				
+			} catch (IllegalAccessException e) {
+				Log.e(TAG, "Call to SSLSocket#setHost(String) failed ", e);
+				
+			} catch (InvocationTargetException e) {
+				Log.e(TAG, "Call to SSLSocket#setHost(String) failed ", e);
+			}
+		} else {
+			Log.i(TAG, "SNI not supported");
+		}
+	}
+
+	
+	/**
+	 * Gets the method to invoke trying to minimize the effective 
+	 * application of reflection.
+	 * 
+	 * @param 	sslSocket		Instance of the SSL socket to use in connection with server.
+	 * @return					Method to call to indicate the server name of interest to the server.
+	 */
+	private static Method getMethod(SSLSocket sslSocket) {
+		final Class<?> sslSocketClass = sslSocket.getClass();
+		final ServerNameIndicator instance = mSingleInstance.get();
+		if (instance == null) {
+			return initFrom(sslSocketClass);
+			
+		} else if (instance.mSSLSocketClassRef.get() != sslSocketClass) {
+			// the underlying class changed
+			return initFrom(sslSocketClass);
+				
+		} else if (instance.mSetHostnameMethodRef == null) {
+			// SNI not supported
+			return null;
+				
+		} else {
+			final Method cachedSetHostnameMethod = instance.mSetHostnameMethodRef.get();
+			return (cachedSetHostnameMethod == null) ? initFrom(sslSocketClass) : cachedSetHostnameMethod;
+		}
+	}
+
+
+	/**
+	 * Singleton initializer.
+	 * 
+	 * Uses reflection to extract and 'cache' the method to invoke to indicate the desited host name to the server side.
+	 *  
+	 * @param 	sslSocketClass		Underlying class providing the implementation of {@link SSLSocket}.
+	 * @return						Method to call to indicate the server name of interest to the server.
+	 */
+	private static Method initFrom(Class<?> sslSocketClass) {
+        Log.i(TAG, "SSLSocket implementation: " + sslSocketClass.getCanonicalName());
+		Method setHostnameMethod = null;
+		try {
+			setHostnameMethod = sslSocketClass.getMethod(METHOD_NAME, String.class);
+		} catch (SecurityException e) {
+			Log.e(TAG, "Could not access to SSLSocket#setHostname(String) method ", e);
+			
+		} catch (NoSuchMethodException e) {
+			Log.i(TAG, "Could not find SSLSocket#setHostname(String) method - SNI not supported");
+		}
+		mSingleInstance.set(new ServerNameIndicator(sslSocketClass, setHostnameMethod));
+		return setHostnameMethod;
+	}
+
+}

+ 2 - 2
oc_jb_workaround/AndroidManifest.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.owncloud.android.workaround.accounts"
     package="com.owncloud.android.workaround.accounts"
-    android:versionCode="0100010"
-    android:versionName="1.0.10" >
+    android:versionCode="0100011"
+    android:versionName="1.0.11" >
 
 
     <uses-sdk
     <uses-sdk
         android:minSdkVersion="16"
         android:minSdkVersion="16"

BIN
oc_jb_workaround/res/drawable-xhdpi/main_app_icon.png


BIN
oc_jb_workaround/res/drawable-xhdpi/workaround_app_icon.png


+ 16 - 6
pom.xml

@@ -5,13 +5,17 @@
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.owncloud.android</groupId>
     <groupId>com.owncloud.android</groupId>
     <artifactId>owncloud</artifactId>
     <artifactId>owncloud</artifactId>
-    <version>1.3.21-SNAPSHOT</version>
+    <version>${owncloud.version}</version>
     <packaging>apk</packaging>
     <packaging>apk</packaging>
     <name>Owncloud Android</name>
     <name>Owncloud Android</name>
 
 
     <properties>
     <properties>
+        <owncloud.version>1.5.1-SNAPSHOT</owncloud.version>
         <java-version>1.6</java-version>
         <java-version>1.6</java-version>
-        <google.android-version>4.2.2_r2</google.android-version>
+        <!-- Given by maven-android-sdk-deployer -->
+        <google.android-version>4.4_r1</google.android-version>
+        <!-- Usually the latest Android API -->
+        <google.android-api>19</google.android-api>
         <actionbarsherlock-version>4.2.0</actionbarsherlock-version>
         <actionbarsherlock-version>4.2.0</actionbarsherlock-version>
     </properties>
     </properties>
 
 
@@ -22,7 +26,7 @@
         <developerConnection>scm:git:git@github.com:owncloud/android.git</developerConnection>
         <developerConnection>scm:git:git@github.com:owncloud/android.git</developerConnection>
         <url>https://github.com/owncloud/android</url>
         <url>https://github.com/owncloud/android</url>
     </scm>
     </scm>
-
+    
     <dependencies>
     <dependencies>
 
 
         <dependency>
         <dependency>
@@ -51,6 +55,13 @@
             <artifactId>jackrabbit-webdav</artifactId>
             <artifactId>jackrabbit-webdav</artifactId>
             <version>2.5.2</version>
             <version>2.5.2</version>
         </dependency>
         </dependency>
+        
+        <!-- MUST BE INSTALLED FIRST: cd oc_framework; mvn install -->
+        <dependency>
+         <groupId>com.owncloud.android</groupId>
+         <artifactId>oc_framework</artifactId>
+         <version>${owncloud.version}</version>
+      </dependency>
 
 
     </dependencies>
     </dependencies>
 
 
@@ -74,12 +85,11 @@
             <plugin>
             <plugin>
                 <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                 <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                 <artifactId>android-maven-plugin</artifactId>
                 <artifactId>android-maven-plugin</artifactId>
-                <version>3.5.0</version>
+                <version>3.8.0</version>
                 <configuration>
                 <configuration>
                     <sdk>
                     <sdk>
-                        <!-- platform or api level (api level 4 = platform 1.6)-->
                         <path>${env.ANDROID_HOME}</path>
                         <path>${env.ANDROID_HOME}</path>
-                        <platform>17</platform>
+                        <platform>${google.android-api}</platform>
                     </sdk>
                     </sdk>
                 </configuration>
                 </configuration>
                 <extensions>true</extensions>
                 <extensions>true</extensions>

BIN
res/drawable-hdpi/winter_holidays_icon.png


BIN
res/drawable-ldpi/winter_holidays_icon.png


BIN
res/drawable-mdpi/winter_holidays_icon.png


+ 30 - 24
res/layout-land/account_setup.xml

@@ -27,7 +27,7 @@
     <LinearLayout
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_above="@id/buttonOK"
+        android:layout_above="@+id/bottom_block"
         android:layout_alignParentTop="true"
         android:layout_alignParentTop="true"
         android:orientation="horizontal" >
         android:orientation="horizontal" >
         
         
@@ -186,27 +186,33 @@
 						           
 						           
 	</LinearLayout>
 	</LinearLayout>
        
        
-	<Button
-	    android:id="@id/buttonOK"
-	    android:layout_width="match_parent"
-	    android:layout_height="wrap_content"
-	    android:layout_above="@+id/welcome_link"
-	    android:layout_centerHorizontal="true"
-	    android:enabled="false"
-	    android:onClick="onOkClick"
-	    android:text="@string/setup_btn_connect" />
-
-	<Button
-	    android:id="@id/welcome_link"
-	    android:layout_width="wrap_content"
-	    android:layout_height="wrap_content"
-	    android:layout_alignParentBottom="true"
-	    android:layout_centerHorizontal="true"
-	    android:background="@android:color/transparent"
-	    android:onClick="onRegisterClick"
-	    android:paddingBottom="5dp"
-	    android:paddingTop="5dp"
-	    android:text="@string/auth_register"
-	    android:textColor="#0000FF"/>
-	
+    <LinearLayout
+        android:id="@id/bottom_block"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+		android:layout_alignParentBottom="true"
+        android:orientation="vertical" >
+        
+		<Button
+		    android:id="@+id/buttonOK"
+		    android:layout_width="match_parent"
+		    android:layout_height="wrap_content"
+		    android:layout_gravity="center_horizontal"
+		    android:enabled="false"
+		    android:onClick="onOkClick"
+		    android:text="@string/setup_btn_connect" />
+		
+		<Button
+		    android:id="@+id/welcome_link"
+		    android:layout_width="wrap_content"
+		    android:layout_height="wrap_content"
+		    android:layout_gravity="center_horizontal"
+		    android:background="@android:color/transparent"
+		    android:onClick="onRegisterClick"
+		    android:paddingBottom="5dp"
+		    android:paddingTop="5dp"
+		    android:text="@string/auth_register"
+		    android:textColor="#0000FF"/>
+	</LinearLayout>
+		
 </RelativeLayout>
 </RelativeLayout>

+ 2 - 0
res/values-az/strings.xml

@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>

+ 2 - 0
res/values-ca/strings.xml

@@ -175,6 +175,7 @@
   <string name="sync_file_fail_msg">L\'arxiu remot no ha pogut ser comprovat</string>
   <string name="sync_file_fail_msg">L\'arxiu remot no ha pogut ser comprovat</string>
   <string name="sync_file_nothing_to_do_msg">Contingut de l\'arxiu ja sincronitzat</string>
   <string name="sync_file_nothing_to_do_msg">Contingut de l\'arxiu ja sincronitzat</string>
   <string name="create_dir_fail_msg">La carpeta no s\'ha pogut crear</string>
   <string name="create_dir_fail_msg">La carpeta no s\'ha pogut crear</string>
+  <string name="filename_forbidden_characters">Caràcters no permesos: / \\ &lt; &gt; : \" | ? *</string>
   <string name="wait_a_moment">Espereu</string>
   <string name="wait_a_moment">Espereu</string>
   <string name="filedisplay_unexpected_bad_get_content">S\'ha produït un problema inesperat; proveu una altra aplicació per seleccionar el fitxer</string>
   <string name="filedisplay_unexpected_bad_get_content">S\'ha produït un problema inesperat; proveu una altra aplicació per seleccionar el fitxer</string>
   <string name="filedisplay_no_file_selected">No heu seleccionat cap fitxer</string>
   <string name="filedisplay_no_file_selected">No heu seleccionat cap fitxer</string>
@@ -218,6 +219,7 @@
   <string name="preview_image_description">Visualització prèvia d\'imatge</string>
   <string name="preview_image_description">Visualització prèvia d\'imatge</string>
   <string name="preview_image_error_unknown_format">Auquesta imatge no es pot mostrar</string>
   <string name="preview_image_error_unknown_format">Auquesta imatge no es pot mostrar</string>
   <string name="error__upload__local_file_not_copied">%1$s no s\'ha pogut copiar a la carpeta local %2$s</string>
   <string name="error__upload__local_file_not_copied">%1$s no s\'ha pogut copiar a la carpeta local %2$s</string>
+  <string name="actionbar_failed_instant_upload">La pujada instantània ha fallat</string>
   <string name="failed_upload_headline_text">Fallada de pujades instantànies</string>
   <string name="failed_upload_headline_text">Fallada de pujades instantànies</string>
   <string name="failed_upload_headline_hint">Resum de totes les pujades instantànies que han fallat</string>
   <string name="failed_upload_headline_hint">Resum de totes les pujades instantànies que han fallat</string>
   <string name="failed_upload_all_cb">selecciona-ho tot</string>
   <string name="failed_upload_all_cb">selecciona-ho tot</string>

+ 7 - 7
res/values-cs-rCZ/strings.xml

@@ -27,7 +27,7 @@
   <string name="prefs_recommend">Doporučit příteli</string>
   <string name="prefs_recommend">Doporučit příteli</string>
   <string name="prefs_feedback">Odezva</string>
   <string name="prefs_feedback">Odezva</string>
   <string name="prefs_imprint">Imprint</string>
   <string name="prefs_imprint">Imprint</string>
-  <string name="recommend_subject">Zkusit %1$s na vašem smartphonu!</string>
+  <string name="recommend_subject">Zkuste %1$s na vašem smartphonu!</string>
   <string name="recommend_text">Chtěl bych vás pozvat k používání %1$s na vašem smartphonu.\nKe stažení zde:  %2$s</string>
   <string name="recommend_text">Chtěl bych vás pozvat k používání %1$s na vašem smartphonu.\nKe stažení zde:  %2$s</string>
   <string name="auth_check_server">Zkontrolovat server</string>
   <string name="auth_check_server">Zkontrolovat server</string>
   <string name="auth_host_url">Adresa serveru</string>
   <string name="auth_host_url">Adresa serveru</string>
@@ -95,7 +95,7 @@
   <string name="sync_foreign_files_forgotten_ticker">Některé místní soubory byly zapomenuty</string>
   <string name="sync_foreign_files_forgotten_ticker">Některé místní soubory byly zapomenuty</string>
   <string name="sync_foreign_files_forgotten_content">%1$d souborů z adresáře %2$s nelze zkopírovat do</string>
   <string name="sync_foreign_files_forgotten_content">%1$d souborů z adresáře %2$s nelze zkopírovat do</string>
   <string name="sync_foreign_files_forgotten_explanation">Od verze 1.3.16 jsou soubory odeslané z tohoto zařízení, pro ochranu proti ztrátě dat při synchronizaci z více účtů, nahrány do místní složky %1$s.\n\nVšechny soubory odeslané předchozími verzemi byly kvůli této změně přesunuty do složky %2$s. Bohužel chyba zabránila dokončení této operace při synchronizaci účtu. Můžete nyní ponechat soubory ve stávajícím stavu a smazat odkaz na %3$s nebo přesunout soubory do adresáře %1$s a zachovat odkazy na %4$s.\n\nNásleduje seznam místních souborů a jejich odkazů na vzdálené soubory v %5$s.</string>
   <string name="sync_foreign_files_forgotten_explanation">Od verze 1.3.16 jsou soubory odeslané z tohoto zařízení, pro ochranu proti ztrátě dat při synchronizaci z více účtů, nahrány do místní složky %1$s.\n\nVšechny soubory odeslané předchozími verzemi byly kvůli této změně přesunuty do složky %2$s. Bohužel chyba zabránila dokončení této operace při synchronizaci účtu. Můžete nyní ponechat soubory ve stávajícím stavu a smazat odkaz na %3$s nebo přesunout soubory do adresáře %1$s a zachovat odkazy na %4$s.\n\nNásleduje seznam místních souborů a jejich odkazů na vzdálené soubory v %5$s.</string>
-  <string name="sync_current_folder_was_removed">Složka %1$s již neexistuje.</string>
+  <string name="sync_current_folder_was_removed">Složka %1$s již neexistuje</string>
   <string name="foreign_files_move">Přesunout vše</string>
   <string name="foreign_files_move">Přesunout vše</string>
   <string name="foreign_files_success">Všechny soubory byly přesunuty</string>
   <string name="foreign_files_success">Všechny soubory byly přesunuty</string>
   <string name="foreign_files_fail">Některé soubory nebylo možno přesunout</string>
   <string name="foreign_files_fail">Některé soubory nebylo možno přesunout</string>
@@ -121,7 +121,7 @@
   <string name="media_err_unsupported">Nepodporovaný kodek</string>
   <string name="media_err_unsupported">Nepodporovaný kodek</string>
   <string name="media_err_io">Multimediální soubor nelze přečíst</string>
   <string name="media_err_io">Multimediální soubor nelze přečíst</string>
   <string name="media_err_malformed">Multimediální soubor není správně kódován</string>
   <string name="media_err_malformed">Multimediální soubor není správně kódován</string>
-  <string name="media_err_timeout">Příliš mnoho času bylo zkoušeno přehrání</string>
+  <string name="media_err_timeout">Vypršel čas při pokusu o přehrání</string>
   <string name="media_err_invalid_progressive_playback">Multimediální soubor nelze proudově odesílat</string>
   <string name="media_err_invalid_progressive_playback">Multimediální soubor nelze proudově odesílat</string>
   <string name="media_err_unknown">Multimediální soubor nemůže být přehrán s výchozím přehrávačem</string>
   <string name="media_err_unknown">Multimediální soubor nemůže být přehrán s výchozím přehrávačem</string>
   <string name="media_err_security_ex">Chyba zabezpečení při pokusu o přehrání %1$s</string>
   <string name="media_err_security_ex">Chyba zabezpečení při pokusu o přehrání %1$s</string>
@@ -156,8 +156,8 @@
   <string name="auth_expired_basic_auth_toast">Zadejte prosím aktuální heslo</string>
   <string name="auth_expired_basic_auth_toast">Zadejte prosím aktuální heslo</string>
   <string name="auth_expired_saml_sso_token_toast">Vaše přihlášení vypršelo. Přihlašte se, prosím, znovu</string>
   <string name="auth_expired_saml_sso_token_toast">Vaše přihlášení vypršelo. Přihlašte se, prosím, znovu</string>
   <string name="auth_connecting_auth_server">Připojuji se k přihlašovacímu serveru...</string>
   <string name="auth_connecting_auth_server">Připojuji se k přihlašovacímu serveru...</string>
-  <string name="auth_unsupported_auth_method">Server nepodporuje tuto přihlašovací metodu.</string>
-  <string name="auth_unsupported_multiaccount">%1$s nepodporuje více účtů.</string>
+  <string name="auth_unsupported_auth_method">Server nepodporuje tuto přihlašovací metodu</string>
+  <string name="auth_unsupported_multiaccount">%1$s nepodporuje více účtů</string>
   <string name="fd_keep_in_sync">Udržovat soubor aktuální</string>
   <string name="fd_keep_in_sync">Udržovat soubor aktuální</string>
   <string name="common_rename">Přejmenovat</string>
   <string name="common_rename">Přejmenovat</string>
   <string name="common_remove">Odstranit</string>
   <string name="common_remove">Odstranit</string>
@@ -179,7 +179,7 @@
   <string name="wait_a_moment">Počkejte chvíli</string>
   <string name="wait_a_moment">Počkejte chvíli</string>
   <string name="filedisplay_unexpected_bad_get_content">Neočekávaný problém - zkuste zvolit soubor jinou aplikací</string>
   <string name="filedisplay_unexpected_bad_get_content">Neočekávaný problém - zkuste zvolit soubor jinou aplikací</string>
   <string name="filedisplay_no_file_selected">Žádný soubor nebyl vybrán</string>
   <string name="filedisplay_no_file_selected">Žádný soubor nebyl vybrán</string>
-  <string name="oauth_check_onoff">Přihlásit se s oAuth2.</string>
+  <string name="oauth_check_onoff">Přihlásit se s oAuth2</string>
   <string name="oauth_login_connection">Připojuji se k oAuth2 serveru...</string>
   <string name="oauth_login_connection">Připojuji se k oAuth2 serveru...</string>
   <string name="ssl_validator_header">Identitu stránky nelze ověřit</string>
   <string name="ssl_validator_header">Identitu stránky nelze ověřit</string>
   <string name="ssl_validator_reason_cert_not_trusted">- Certifikát serveru je nedůvěryhodný</string>
   <string name="ssl_validator_reason_cert_not_trusted">- Certifikát serveru je nedůvěryhodný</string>
@@ -219,7 +219,7 @@
   <string name="preview_image_description">Náhled obrázku</string>
   <string name="preview_image_description">Náhled obrázku</string>
   <string name="preview_image_error_unknown_format">Obrázek nemůže být zobrazen</string>
   <string name="preview_image_error_unknown_format">Obrázek nemůže být zobrazen</string>
   <string name="error__upload__local_file_not_copied">%1$s nelze zkopírovat do místního adresáře %2$s</string>
   <string name="error__upload__local_file_not_copied">%1$s nelze zkopírovat do místního adresáře %2$s</string>
-  <string name="actionbar_failed_instant_upload">Selhalo Okamžité odeslání\"</string>
+  <string name="actionbar_failed_instant_upload">Selhalo Okamžité odeslání</string>
   <string name="failed_upload_headline_text">Selhaná okamžitá odeslání</string>
   <string name="failed_upload_headline_text">Selhaná okamžitá odeslání</string>
   <string name="failed_upload_headline_hint">Souhrn všech selhaných okamžitých odeslání</string>
   <string name="failed_upload_headline_hint">Souhrn všech selhaných okamžitých odeslání</string>
   <string name="failed_upload_all_cb">vybrat vše</string>
   <string name="failed_upload_all_cb">vybrat vše</string>

+ 4 - 4
res/values-de/strings.xml

@@ -28,7 +28,7 @@
   <string name="prefs_feedback">Rückmeldungen</string>
   <string name="prefs_feedback">Rückmeldungen</string>
   <string name="prefs_imprint">Impressum</string>
   <string name="prefs_imprint">Impressum</string>
   <string name="recommend_subject">Probiere %1$s auf Deinem Smartphone!</string>
   <string name="recommend_subject">Probiere %1$s auf Deinem Smartphone!</string>
-  <string name="recommend_text">Ich möchte Dich zum Benutzen von %1$s auf Deinem Smartphone einladen!\nLade es hier herunter: %2$s</string>
+  <string name="recommend_text">Ich möchte Dich zu %1$s für Dein Smartphone einladen!\nLade es hier herunter: %2$s</string>
   <string name="auth_check_server">Überprüfe den Server</string>
   <string name="auth_check_server">Überprüfe den Server</string>
   <string name="auth_host_url">Adresse des Servers</string>
   <string name="auth_host_url">Adresse des Servers</string>
   <string name="auth_username">Benutzername</string>
   <string name="auth_username">Benutzername</string>
@@ -94,7 +94,7 @@
   <string name="sync_fail_in_favourites_content">Inhalte von %1$d konnte nicht synchronisiert werden (%2$d Konflikte)</string>
   <string name="sync_fail_in_favourites_content">Inhalte von %1$d konnte nicht synchronisiert werden (%2$d Konflikte)</string>
   <string name="sync_foreign_files_forgotten_ticker">Einige lokale Dateien wurden vergessen</string>
   <string name="sync_foreign_files_forgotten_ticker">Einige lokale Dateien wurden vergessen</string>
   <string name="sync_foreign_files_forgotten_content">%1$d Dateien aus dem Verzeichnis %2$s konnten nicht kopiert werden nach</string>
   <string name="sync_foreign_files_forgotten_content">%1$d Dateien aus dem Verzeichnis %2$s konnten nicht kopiert werden nach</string>
-  <string name="sync_foreign_files_forgotten_explanation">\"Mit Version 1.3.16 werden Dateien die von diesem Gerät aus hochgeladen werden in den lokalen Ordner %1$s kopiert um Datenverlust zu vermeiden, wenn eine einzelne Datei mit mehreren Accounts synchronisiert wird.\n\nInfolge dieser Änderung wurden alle Dateien, die mit vorherigen Versionen dieser App hochgeladen wurden, in den Ordner %2$s verschoben. Jedoch ist während der Account-Synchronisation ein Fehler aufgetreten, der das Abschließen dieses Vorgangs verhindert. Du kannst die Datei(en) entweder wie sie sind belassen und den Link zu %3$s entfernen oder die Datei(en) in den %1$s Ordner verschieben und  den Link zu %4$s beibehalten.\n\nUnten befindet sich eine Liste der lokalen Datei(en) und der mit ihnen verbundenen Remote-Datei(en) in %5$s.</string>
+  <string name="sync_foreign_files_forgotten_explanation">\"Mit Version 1.3.16 werden Dateien die von diesem Gerät aus hochgeladen werden in den lokalen Ordner %1$s kopiert, um Datenverlust zu vermeiden, wenn eine einzelne Datei mit mehreren Accounts synchronisiert wird.\n\nInfolge dieser Änderung wurden alle Dateien, die mit vorherigen Versionen dieser App hochgeladen wurden, in den Ordner %2$s verschoben. Jedoch ist während der Account-Synchronisation ein Fehler aufgetreten, der das Abschließen dieses Vorgangs verhindert. Du kannst die Datei(en) entweder wie sie sind belassen und den Link zu %3$s entfernen, oder die Datei(en) in den %1$s Ordner verschieben, und  den Link zu %4$s beibehalten.\n\nUnten befindet sich eine Liste der lokalen Datei(en) und der mit ihnen verbundenen Remote-Datei(en) in %5$s.</string>
   <string name="sync_current_folder_was_removed">Das Verzeichnis %1$s existiert nicht mehr</string>
   <string name="sync_current_folder_was_removed">Das Verzeichnis %1$s existiert nicht mehr</string>
   <string name="foreign_files_move">Verschiebe alle</string>
   <string name="foreign_files_move">Verschiebe alle</string>
   <string name="foreign_files_success">Alle Dateien wurden verschoben</string>
   <string name="foreign_files_success">Alle Dateien wurden verschoben</string>
@@ -152,9 +152,9 @@
   <string name="auth_oauth_error">Autorisierung nicht erfolgreich</string>
   <string name="auth_oauth_error">Autorisierung nicht erfolgreich</string>
   <string name="auth_oauth_error_access_denied">Zugriff durch den Autorisierungsserver abgelehnt</string>
   <string name="auth_oauth_error_access_denied">Zugriff durch den Autorisierungsserver abgelehnt</string>
   <string name="auth_wtf_reenter_URL">Unerwarteter Zustand; bitte gib die URL des Servers nochmals ein</string>
   <string name="auth_wtf_reenter_URL">Unerwarteter Zustand; bitte gib die URL des Servers nochmals ein</string>
-  <string name="auth_expired_oauth_token_toast">Ihre Autorisierung ist abgelaufen. Bitte Autorisierung nochmals durchführen</string>
+  <string name="auth_expired_oauth_token_toast">Deine Autorisierung ist abgelaufen. Bitte Autorisierung nochmals durchführen</string>
   <string name="auth_expired_basic_auth_toast">Bitte gib dein aktuelles Passwort ein</string>
   <string name="auth_expired_basic_auth_toast">Bitte gib dein aktuelles Passwort ein</string>
-  <string name="auth_expired_saml_sso_token_toast">Ihre Sitzung ist abgelaufen. Bitte Anmeldung nochmals durchführen</string>
+  <string name="auth_expired_saml_sso_token_toast">Deine Sitzung ist abgelaufen. Bitte Anmeldung nochmals durchführen</string>
   <string name="auth_connecting_auth_server">Verbinde mit dem Authentifizierung-Server…</string>
   <string name="auth_connecting_auth_server">Verbinde mit dem Authentifizierung-Server…</string>
   <string name="auth_unsupported_auth_method">Der Server unterstützt diese Authentifizierung-Methode nicht</string>
   <string name="auth_unsupported_auth_method">Der Server unterstützt diese Authentifizierung-Methode nicht</string>
   <string name="auth_unsupported_multiaccount">%1$s unterstützt nicht mehrere Benutzerkonten</string>
   <string name="auth_unsupported_multiaccount">%1$s unterstützt nicht mehrere Benutzerkonten</string>

+ 1 - 0
res/values-es/strings.xml

@@ -219,6 +219,7 @@
   <string name="preview_image_description">Previsualización de imagen</string>
   <string name="preview_image_description">Previsualización de imagen</string>
   <string name="preview_image_error_unknown_format">No se puede mostrar la imagen</string>
   <string name="preview_image_error_unknown_format">No se puede mostrar la imagen</string>
   <string name="error__upload__local_file_not_copied">%1$s no puede ser copiado al %2$s directorio local </string>
   <string name="error__upload__local_file_not_copied">%1$s no puede ser copiado al %2$s directorio local </string>
+  <string name="actionbar_failed_instant_upload">Carga instantánea fallida</string>
   <string name="failed_upload_headline_text">Cargas instantáneas fallidas</string>
   <string name="failed_upload_headline_text">Cargas instantáneas fallidas</string>
   <string name="failed_upload_headline_hint">Resumen de todas las cargas instantáneas fallidas</string>
   <string name="failed_upload_headline_hint">Resumen de todas las cargas instantáneas fallidas</string>
   <string name="failed_upload_all_cb">Seleccionar todos</string>
   <string name="failed_upload_all_cb">Seleccionar todos</string>

+ 18 - 0
res/values-eu/strings.xml

@@ -24,7 +24,11 @@
   <string name="prefs_log_summary_history">Honek gordetako erregistroak bistaratzen ditu.</string>
   <string name="prefs_log_summary_history">Honek gordetako erregistroak bistaratzen ditu.</string>
   <string name="prefs_log_delete_history_button">Ezabatu historia</string>
   <string name="prefs_log_delete_history_button">Ezabatu historia</string>
   <string name="prefs_help">Laguntza</string>
   <string name="prefs_help">Laguntza</string>
+  <string name="prefs_recommend">Lagun bati aholkatu</string>
+  <string name="prefs_feedback">Oharrak</string>
   <string name="prefs_imprint">Imprint</string>
   <string name="prefs_imprint">Imprint</string>
+  <string name="recommend_subject">Probatu %1$s zure telefono adimentsuan!</string>
+  <string name="recommend_text">Nik %1$s zure telefono adimentsuan erabitzera gonbidatu nahi zaitut!\nDeskargatu hemen: %2$s</string>
   <string name="auth_check_server">Egiaztatu zerbitzaria</string>
   <string name="auth_check_server">Egiaztatu zerbitzaria</string>
   <string name="auth_host_url">Zerbitzariaren helbidea</string>
   <string name="auth_host_url">Zerbitzariaren helbidea</string>
   <string name="auth_username">Erabiltzaile izena</string>
   <string name="auth_username">Erabiltzaile izena</string>
@@ -90,6 +94,8 @@
   <string name="sync_fail_in_favourites_content">%1$d fitxategien edukiak ezin dira sinkronizatu (%2$d gatazka)</string>
   <string name="sync_fail_in_favourites_content">%1$d fitxategien edukiak ezin dira sinkronizatu (%2$d gatazka)</string>
   <string name="sync_foreign_files_forgotten_ticker">Bertako fitxategi batzuk ahaztu dira</string>
   <string name="sync_foreign_files_forgotten_ticker">Bertako fitxategi batzuk ahaztu dira</string>
   <string name="sync_foreign_files_forgotten_content">%2$s karpetako %1$d fitxategi ezin dira dira kopiatu</string>
   <string name="sync_foreign_files_forgotten_content">%2$s karpetako %1$d fitxategi ezin dira dira kopiatu</string>
+  <string name="sync_foreign_files_forgotten_explanation">1.3.16 bertsioan, gailu honetatik igotzen diren fitxategiak bertako %1$s karpetara mugitzen dira datu galera ekiditzeko fitxategi bat kontu ezberdinekin sinkronizatzen denean.\n\nAldaketa hau dela eta, programa honen aurreko bertsioetan igotako fitxategi guztiak %2$s karpetara kopiatu dira. Hala ere, errore batek hau burutzea ekidin du kontuaren sinkronizazioa egiten ari zen bitartean. Orain fitxategiak dauden bezala utz ditzakezu eta %3$s rako lotura ezabatu, edo fitxategiak %1$s karpetara mugi ditzakezu eta %4$srako lotura mantendu.\n\nBehean bertako fitxategien zerrenda eta %5$s era lotuta zeuden urruneko fitxategiena.</string>
+  <string name="sync_current_folder_was_removed">%1$s karpeta dagoeneko ez da existitzen</string>
   <string name="foreign_files_move">Mugitu denak</string>
   <string name="foreign_files_move">Mugitu denak</string>
   <string name="foreign_files_success">Fitxategi guztiak mugitu dira</string>
   <string name="foreign_files_success">Fitxategi guztiak mugitu dira</string>
   <string name="foreign_files_fail">Fitxategi batzuk ezin dira mugitu</string>
   <string name="foreign_files_fail">Fitxategi batzuk ezin dira mugitu</string>
@@ -115,6 +121,7 @@
   <string name="media_err_unsupported">Onartzen ez de euskarri kodeka</string>
   <string name="media_err_unsupported">Onartzen ez de euskarri kodeka</string>
   <string name="media_err_io">Euskarri fitxategia ezin da bihurtu</string>
   <string name="media_err_io">Euskarri fitxategia ezin da bihurtu</string>
   <string name="media_err_malformed">Euskarri fitxategia ezin da kodetu</string>
   <string name="media_err_malformed">Euskarri fitxategia ezin da kodetu</string>
+  <string name="media_err_timeout">Erreproduzitzen saiatzean denbora iraungitu da</string>
   <string name="media_err_invalid_progressive_playback">Euskarri fitxategia ezin da jariotu</string>
   <string name="media_err_invalid_progressive_playback">Euskarri fitxategia ezin da jariotu</string>
   <string name="media_err_unknown">Euskarri fitxategia ezin erreproduzitu stock euskarri erreproduzigailuarekin</string>
   <string name="media_err_unknown">Euskarri fitxategia ezin erreproduzitu stock euskarri erreproduzigailuarekin</string>
   <string name="media_err_security_ex">Segurtasun errorea %1$s erreproduzitzen saiatzean</string>
   <string name="media_err_security_ex">Segurtasun errorea %1$s erreproduzitzen saiatzean</string>
@@ -129,12 +136,15 @@
   <string name="auth_connection_established">Konexioa ezarri da</string>
   <string name="auth_connection_established">Konexioa ezarri da</string>
   <string name="auth_testing_connection">Konexioa probatzen...</string>
   <string name="auth_testing_connection">Konexioa probatzen...</string>
   <string name="auth_not_configured_title">gaizki egindako server konfigurazioa</string>
   <string name="auth_not_configured_title">gaizki egindako server konfigurazioa</string>
+  <string name="auth_account_not_new">Erabiltzaile eta zerbitzari hauendako dagoeneko kontu bat  existitzen da gailu honetan</string>
+  <string name="auth_account_not_the_same">Sartutako erabiltzaileak ez du bat egiten kontu honetako erabiltzailearekin</string>
   <string name="auth_unknown_error_title">Errore ezezagun bat gertatu da</string>
   <string name="auth_unknown_error_title">Errore ezezagun bat gertatu da</string>
   <string name="auth_unknown_host_title">Ezin izan da hostalaria aurkitu</string>
   <string name="auth_unknown_host_title">Ezin izan da hostalaria aurkitu</string>
   <string name="auth_incorrect_path_title">ez da serveren instalaziorik aurkitu</string>
   <string name="auth_incorrect_path_title">ez da serveren instalaziorik aurkitu</string>
   <string name="auth_timeout_title">Zerbitzariak denbora asko hartu du erantzuteko</string>
   <string name="auth_timeout_title">Zerbitzariak denbora asko hartu du erantzuteko</string>
   <string name="auth_incorrect_address_title">Gaizki sortutako URLa</string>
   <string name="auth_incorrect_address_title">Gaizki sortutako URLa</string>
   <string name="auth_ssl_general_error_title">SSL abiaratzeak huts egin du</string>
   <string name="auth_ssl_general_error_title">SSL abiaratzeak huts egin du</string>
+  <string name="auth_ssl_unverified_server_title">Ezin izan da SSL zerbitzariaren identitaea egiaztatu</string>
   <string name="auth_bad_oc_version_title">server zerbitzari bertsio ezezaguna</string>
   <string name="auth_bad_oc_version_title">server zerbitzari bertsio ezezaguna</string>
   <string name="auth_wrong_connection_title">Ezin izan da konexioa egin</string>
   <string name="auth_wrong_connection_title">Ezin izan da konexioa egin</string>
   <string name="auth_secure_connection">Konexio segurua ezarri da</string>
   <string name="auth_secure_connection">Konexio segurua ezarri da</string>
@@ -142,7 +152,12 @@
   <string name="auth_oauth_error">Baimena ez da lortu</string>
   <string name="auth_oauth_error">Baimena ez da lortu</string>
   <string name="auth_oauth_error_access_denied">Sarrera autorizazio zerbitzariak ukatua</string>
   <string name="auth_oauth_error_access_denied">Sarrera autorizazio zerbitzariak ukatua</string>
   <string name="auth_wtf_reenter_URL">Egoera esperogabea, mesedez idatzi berriz zerbitzari URLa</string>
   <string name="auth_wtf_reenter_URL">Egoera esperogabea, mesedez idatzi berriz zerbitzari URLa</string>
+  <string name="auth_expired_oauth_token_toast">Zure baimena iraungitu da.\nMesedez, baimendu berriz</string>
   <string name="auth_expired_basic_auth_toast">Mesedez, sartu oraingo pasahitza</string>
   <string name="auth_expired_basic_auth_toast">Mesedez, sartu oraingo pasahitza</string>
+  <string name="auth_expired_saml_sso_token_toast">Zure saioa iraungitu da. Mesdez konektatu berriro</string>
+  <string name="auth_connecting_auth_server">Konektatzen autentikazio zerbitzarira...</string>
+  <string name="auth_unsupported_auth_method">Zerbitzariak ez du autentikazio metodo hau onartzen</string>
+  <string name="auth_unsupported_multiaccount">%1$s ez du kontu anitzak onartzen</string>
   <string name="fd_keep_in_sync">Mantendu fitxategia eguneratuta</string>
   <string name="fd_keep_in_sync">Mantendu fitxategia eguneratuta</string>
   <string name="common_rename">Berrizendatu</string>
   <string name="common_rename">Berrizendatu</string>
   <string name="common_remove">Ezabatu</string>
   <string name="common_remove">Ezabatu</string>
@@ -160,9 +175,11 @@
   <string name="sync_file_fail_msg">Urruneko fitxategia ezin izan da arakatu</string>
   <string name="sync_file_fail_msg">Urruneko fitxategia ezin izan da arakatu</string>
   <string name="sync_file_nothing_to_do_msg">Fitxategi edukiak dagoeneko sinkronizaturik</string>
   <string name="sync_file_nothing_to_do_msg">Fitxategi edukiak dagoeneko sinkronizaturik</string>
   <string name="create_dir_fail_msg">Karpeta ezin da sortu</string>
   <string name="create_dir_fail_msg">Karpeta ezin da sortu</string>
+  <string name="filename_forbidden_characters">Debekatutako karaktereak: / \\ &lt; &gt; : \" | ? *</string>
   <string name="wait_a_moment">Itxaron momentu bat</string>
   <string name="wait_a_moment">Itxaron momentu bat</string>
   <string name="filedisplay_unexpected_bad_get_content">Ezusteko arazoa; mesedez, saiatu beste app batekin fitxategia hautatzeko</string>
   <string name="filedisplay_unexpected_bad_get_content">Ezusteko arazoa; mesedez, saiatu beste app batekin fitxategia hautatzeko</string>
   <string name="filedisplay_no_file_selected">Ez da fitxategirik hautatu</string>
   <string name="filedisplay_no_file_selected">Ez da fitxategirik hautatu</string>
+  <string name="oauth_check_onoff">Saioa hasi oAuth2-rekin</string>
   <string name="oauth_login_connection">Konektatzen oAuth2 zerbitzarira...</string>
   <string name="oauth_login_connection">Konektatzen oAuth2 zerbitzarira...</string>
   <string name="ssl_validator_header">Lekuaren identitatea ezin da egiaztatu</string>
   <string name="ssl_validator_header">Lekuaren identitatea ezin da egiaztatu</string>
   <string name="ssl_validator_reason_cert_not_trusted">- Zerbitzariaren ziurtagiria ez da fidagarria</string>
   <string name="ssl_validator_reason_cert_not_trusted">- Zerbitzariaren ziurtagiria ez da fidagarria</string>
@@ -202,6 +219,7 @@
   <string name="preview_image_description">Irudi aurreikuspena</string>
   <string name="preview_image_description">Irudi aurreikuspena</string>
   <string name="preview_image_error_unknown_format">Ezin da irudi hau erakutsi</string>
   <string name="preview_image_error_unknown_format">Ezin da irudi hau erakutsi</string>
   <string name="error__upload__local_file_not_copied">%1$s ezin da %2$s bertako karpetara kopiatu</string>
   <string name="error__upload__local_file_not_copied">%1$s ezin da %2$s bertako karpetara kopiatu</string>
+  <string name="actionbar_failed_instant_upload">UnekoIgoerak huts egin du</string>
   <string name="failed_upload_headline_text">Uneko igoerek huts egin dute</string>
   <string name="failed_upload_headline_text">Uneko igoerek huts egin dute</string>
   <string name="failed_upload_headline_hint">Huts egindako igoeren laburpena</string>
   <string name="failed_upload_headline_hint">Huts egindako igoeren laburpena</string>
   <string name="failed_upload_all_cb">Hautatu dena</string>
   <string name="failed_upload_all_cb">Hautatu dena</string>

+ 76 - 0
res/values-id/strings.xml

@@ -2,26 +2,102 @@
 <resources>
 <resources>
   <string name="actionbar_upload">Unggah</string>
   <string name="actionbar_upload">Unggah</string>
   <string name="actionbar_upload_files">Berkas</string>
   <string name="actionbar_upload_files">Berkas</string>
+  <string name="actionbar_mkdir">Buat folder</string>
   <string name="actionbar_settings">pengaturan</string>
   <string name="actionbar_settings">pengaturan</string>
+  <string name="actionbar_see_details">Detail</string>
   <string name="prefs_category_general">umum</string>
   <string name="prefs_category_general">umum</string>
   <string name="prefs_category_more">Lainnya</string>
   <string name="prefs_category_more">Lainnya</string>
+  <string name="prefs_accounts">Akun</string>
+  <string name="prefs_manage_accounts">Kolola Akun</string>
   <string name="prefs_help">Bantuan</string>
   <string name="prefs_help">Bantuan</string>
   <string name="prefs_imprint">Imprint</string>
   <string name="prefs_imprint">Imprint</string>
   <string name="auth_host_url">alamat server</string>
   <string name="auth_host_url">alamat server</string>
   <string name="auth_username">nama pengguna</string>
   <string name="auth_username">nama pengguna</string>
   <string name="auth_password">kata kunci</string>
   <string name="auth_password">kata kunci</string>
   <string name="sync_string_files">Berkas</string>
   <string name="sync_string_files">Berkas</string>
+  <string name="setup_btn_connect">Sambungkan</string>
   <string name="uploader_btn_upload_text">Unggah</string>
   <string name="uploader_btn_upload_text">Unggah</string>
+  <string name="uploader_top_message">Pilih folder unggah</string>
+  <string name="uploader_wrn_no_account_title">Tidak ada akun yang ditemukan</string>
+  <string name="uploader_wrn_no_account_text">Belum ada akun %1$s pada perangkat Anda. Silahkan membuat akun terlebih dahulu.</string>
+  <string name="uploader_wrn_no_account_setup_btn_text">Pengaturan</string>
   <string name="uploader_wrn_no_account_quit_btn_text">Keluar</string>
   <string name="uploader_wrn_no_account_quit_btn_text">Keluar</string>
+  <string name="uploader_info_uploading">Mengunggah</string>
+  <string name="filedetails_select_file">Sentuh pada berkas untuk menampilkan informasi tambahan</string>
+  <string name="filedetails_size">Ukuran:</string>
+  <string name="filedetails_type">Tipe:</string>
+  <string name="filedetails_created">Dibuat:</string>
+  <string name="filedetails_modified">Diubah:</string>
   <string name="filedetails_download">unduh</string>
   <string name="filedetails_download">unduh</string>
+  <string name="filedetails_renamed_in_upload_msg">Berkas diubah namanya menjadi %1$s saat pengunggahan</string>
   <string name="common_yes">Ya</string>
   <string name="common_yes">Ya</string>
   <string name="common_no">Tidak</string>
   <string name="common_no">Tidak</string>
   <string name="common_ok">Oke</string>
   <string name="common_ok">Oke</string>
+  <string name="common_cancel_download">Batal mengunduh</string>
   <string name="common_cancel_upload">Batal mengunggah</string>
   <string name="common_cancel_upload">Batal mengunggah</string>
   <string name="common_cancel">batal</string>
   <string name="common_cancel">batal</string>
+  <string name="common_save_exit">Simpan &amp; Keluar</string>
   <string name="common_error">kesalahan</string>
   <string name="common_error">kesalahan</string>
+  <string name="common_loading">Memuat ...</string>
+  <string name="common_error_unknown">Galat tidak diketahui</string>
+  <string name="about_title">Tentang</string>
   <string name="change_password">Ubah sandi</string>
   <string name="change_password">Ubah sandi</string>
+  <string name="delete_account">Hapus akun</string>
+  <string name="create_account">Buat akun</string>
+  <string name="upload_chooser_title">Unggah dari...</string>
+  <string name="uploader_info_dirname">Nama folder</string>
+  <string name="uploader_upload_in_progress_ticker">Mengungggah...</string>
+  <string name="uploader_upload_in_progress_content">%1$d%% Mengunggah %2$s</string>
+  <string name="uploader_upload_succeeded_ticker">Berhasil mengunggah</string>
+  <string name="uploader_upload_succeeded_content_single">%1$s berhasil diunggah</string>
+  <string name="uploader_upload_failed_ticker">Gagal mengunggah</string>
+  <string name="uploader_upload_failed_content_single">Unggah %1$s tidak selesai</string>
+  <string name="downloader_download_in_progress_ticker">Mengunduh...</string>
+  <string name="downloader_download_in_progress_content">%1$d%% Mengunduh %2$s</string>
+  <string name="downloader_download_succeeded_ticker">Berhasil mengunduh</string>
+  <string name="downloader_download_succeeded_content">%1$s berhasil diunduh</string>
+  <string name="downloader_not_downloaded_yet">Belum diunduh</string>
+  <string name="common_choose_account">Pilih akun</string>
+  <string name="sync_conflicts_in_favourites_ticker">Konflik ditemukan</string>
+  <string name="foreign_files_move">Pindahkan semua</string>
+  <string name="foreign_files_success">Semua berkas sudah dipindahkan</string>
+  <string name="foreign_files_fail">Beberapa berkas tidak dapat dipindahkan</string>
+  <string name="foreign_files_local_text">Lokal: %1$s</string>
+  <string name="pincode_enter_pin_code">Silakan masukkan App PIN</string>
+  <string name="pincode_configure_your_pin">Masukkan App PIN</string>
+  <string name="pincode_reenter_your_pincode">Silakan masukkan ulang App PIN</string>
+  <string name="pincode_remove_your_pincode">Hapus App PIN</string>
+  <string name="pincode_mismatch">App PIN tidak sama</string>
+  <string name="pincode_wrong">App PIN salah</string>
+  <string name="pincode_removed">App PIN dihapus</string>
+  <string name="pincode_stored">App PIN disimpan</string>
+  <string name="media_notif_ticker">Pemutar musik %1$s</string>
+  <string name="media_state_playing">%1$s (dimainkan)</string>
+  <string name="media_state_loading">%1$s (sedang dimuat)</string>
+  <string name="media_err_nothing_to_play">Tidak ditemukan berkas media</string>
+  <string name="media_err_no_account">Tidak ada akun yang diberikan</string>
+  <string name="media_err_not_in_owncloud">Brkas tidak didalam akun yang sah</string>
+  <string name="media_err_unsupported">Kodek media tidak didukung</string>
+  <string name="media_err_io">Berkas media tidak dapat dibaca</string>
+  <string name="auth_no_net_conn_title">Tidak ada koneksi internet</string>
+  <string name="auth_nossl_plain_ok_title">Sambungan aman tidak tersedia</string>
+  <string name="auth_connection_established">Sambungan dibuat</string>
+  <string name="auth_not_configured_title">Konfigurasi server cacat</string>
+  <string name="auth_unknown_error_title">Terjadi kesalahan yang tidak diketahui!</string>
+  <string name="auth_secure_connection">Sambungan aman dibuat</string>
   <string name="common_rename">Ubah nama</string>
   <string name="common_rename">Ubah nama</string>
   <string name="common_remove">hilangkan</string>
   <string name="common_remove">hilangkan</string>
+  <string name="confirmation_remove_alert">Apakah Anda yakin ingin menghapus %1$s ?</string>
+  <string name="remove_success_msg">Penghapusan berhasil</string>
+  <string name="remove_fail_msg">Penghapusan gagal</string>
+  <string name="rename_dialog_title">Masukkan nama baru</string>
+  <string name="sync_file_nothing_to_do_msg">Isi berkas sudah diselaraskan</string>
+  <string name="filedisplay_unexpected_bad_get_content">Masalah tidak terduga, silahkan pilih berkas dari aplikasi yang berbeda</string>
+  <string name="ssl_validator_btn_details_see">Detail</string>
   <string name="ssl_validator_btn_details_hide">sembunyikan</string>
   <string name="ssl_validator_btn_details_hide">sembunyikan</string>
+  <string name="ssl_validator_label_signature">Tanda tangan:</string>
+  <string name="ssl_validator_label_signature_algorithm">Algoritma:</string>
+  <string name="conflict_title">Perbarui benturan</string>
+  <string name="conflict_overwrite">Tindih</string>
+  <string name="conflict_dont_upload">Jangan mengunggah</string>
 </resources>
 </resources>

+ 2 - 0
res/values-ja-rJP/strings.xml

@@ -175,6 +175,7 @@
   <string name="sync_file_fail_msg">リモートファイルをチェックできませんでした</string>
   <string name="sync_file_fail_msg">リモートファイルをチェックできませんでした</string>
   <string name="sync_file_nothing_to_do_msg">ファイルコンテンツはすでに同期されています</string>
   <string name="sync_file_nothing_to_do_msg">ファイルコンテンツはすでに同期されています</string>
   <string name="create_dir_fail_msg">ディレクトリを作成できませんでした</string>
   <string name="create_dir_fail_msg">ディレクトリを作成できませんでした</string>
+  <string name="filename_forbidden_characters">使用できない文字: / \\ &lt; &gt; : \" | ? *</string>
   <string name="wait_a_moment">しばらくお待ちください</string>
   <string name="wait_a_moment">しばらくお待ちください</string>
   <string name="filedisplay_unexpected_bad_get_content">予期せぬ問題;他のアプリでファイルを選択してみてください。</string>
   <string name="filedisplay_unexpected_bad_get_content">予期せぬ問題;他のアプリでファイルを選択してみてください。</string>
   <string name="filedisplay_no_file_selected">ファイルは選択されていません</string>
   <string name="filedisplay_no_file_selected">ファイルは選択されていません</string>
@@ -218,6 +219,7 @@
   <string name="preview_image_description">イメージプレビュー</string>
   <string name="preview_image_description">イメージプレビュー</string>
   <string name="preview_image_error_unknown_format">この画像は表示できません</string>
   <string name="preview_image_error_unknown_format">この画像は表示できません</string>
   <string name="error__upload__local_file_not_copied">%1$s は %2$s ローカルディレクトリにコピー出来ませんでした</string>
   <string name="error__upload__local_file_not_copied">%1$s は %2$s ローカルディレクトリにコピー出来ませんでした</string>
+  <string name="actionbar_failed_instant_upload">インスタントアップロードに失敗</string>
   <string name="failed_upload_headline_text">インスタントアップロードに失敗</string>
   <string name="failed_upload_headline_text">インスタントアップロードに失敗</string>
   <string name="failed_upload_headline_hint">全ての失敗したインスタントアップロードの要約</string>
   <string name="failed_upload_headline_hint">全ての失敗したインスタントアップロードの要約</string>
   <string name="failed_upload_all_cb">すべて選択</string>
   <string name="failed_upload_all_cb">すべて選択</string>

+ 2 - 0
res/values-nl/strings.xml

@@ -175,6 +175,7 @@
   <string name="sync_file_fail_msg">Extern bestand kon niet worden gecontroleerd</string>
   <string name="sync_file_fail_msg">Extern bestand kon niet worden gecontroleerd</string>
   <string name="sync_file_nothing_to_do_msg">Bestandsinhoud is al gesynchroniseerd</string>
   <string name="sync_file_nothing_to_do_msg">Bestandsinhoud is al gesynchroniseerd</string>
   <string name="create_dir_fail_msg">Map kon niet worden aangemaakt</string>
   <string name="create_dir_fail_msg">Map kon niet worden aangemaakt</string>
+  <string name="filename_forbidden_characters">Verboden tekens: / \\ &lt; &gt; : \" | ? *</string>
   <string name="wait_a_moment">Even geduld</string>
   <string name="wait_a_moment">Even geduld</string>
   <string name="filedisplay_unexpected_bad_get_content">Onverwacht probleem; probeer een andere app om het bestand te selecteren</string>
   <string name="filedisplay_unexpected_bad_get_content">Onverwacht probleem; probeer een andere app om het bestand te selecteren</string>
   <string name="filedisplay_no_file_selected">Er werd geen bestand geselecteerd</string>
   <string name="filedisplay_no_file_selected">Er werd geen bestand geselecteerd</string>
@@ -218,6 +219,7 @@
   <string name="preview_image_description">Afbeelding voorbeeld</string>
   <string name="preview_image_description">Afbeelding voorbeeld</string>
   <string name="preview_image_error_unknown_format">Deze afbeelding kan niet weergegeven worden</string>
   <string name="preview_image_error_unknown_format">Deze afbeelding kan niet weergegeven worden</string>
   <string name="error__upload__local_file_not_copied">%1$s kon niet worden gekopieerd naar de %2$s lokale map</string>
   <string name="error__upload__local_file_not_copied">%1$s kon niet worden gekopieerd naar de %2$s lokale map</string>
+  <string name="actionbar_failed_instant_upload">Mislukt InstantUpload</string>
   <string name="failed_upload_headline_text">Mislukte directe uploads</string>
   <string name="failed_upload_headline_text">Mislukte directe uploads</string>
   <string name="failed_upload_headline_hint">Samenvatting van alle mislukte directe uploads</string>
   <string name="failed_upload_headline_hint">Samenvatting van alle mislukte directe uploads</string>
   <string name="failed_upload_all_cb">alles selecteren</string>
   <string name="failed_upload_all_cb">alles selecteren</string>

+ 16 - 0
res/values-pt-rPT/strings.xml

@@ -24,8 +24,11 @@
   <string name="prefs_log_summary_history">Isto mostra os registos guardados</string>
   <string name="prefs_log_summary_history">Isto mostra os registos guardados</string>
   <string name="prefs_log_delete_history_button">Eliminar Histórico</string>
   <string name="prefs_log_delete_history_button">Eliminar Histórico</string>
   <string name="prefs_help">Ajuda</string>
   <string name="prefs_help">Ajuda</string>
+  <string name="prefs_recommend">Recomendar a um amigo</string>
   <string name="prefs_feedback">Resposta</string>
   <string name="prefs_feedback">Resposta</string>
   <string name="prefs_imprint">Imprint</string>
   <string name="prefs_imprint">Imprint</string>
+  <string name="recommend_subject">Experimente %1$s no seu smartphone!</string>
+  <string name="recommend_text">Quero convidá-lo para experimentar %1$s no seu smartphone!\nDescarregue aqui: %2$s</string>
   <string name="auth_check_server">Verificar Servidor</string>
   <string name="auth_check_server">Verificar Servidor</string>
   <string name="auth_host_url">Endereço do servidor</string>
   <string name="auth_host_url">Endereço do servidor</string>
   <string name="auth_username">Nome de Utilizador</string>
   <string name="auth_username">Nome de Utilizador</string>
@@ -92,6 +95,7 @@
   <string name="sync_foreign_files_forgotten_ticker">Alguns ficheiros locais ficaram esquecidos</string>
   <string name="sync_foreign_files_forgotten_ticker">Alguns ficheiros locais ficaram esquecidos</string>
   <string name="sync_foreign_files_forgotten_content">%1$d ficheiros da directoria %2$s não foram copiados</string>
   <string name="sync_foreign_files_forgotten_content">%1$d ficheiros da directoria %2$s não foram copiados</string>
   <string name="sync_foreign_files_forgotten_explanation">Com a versão 1.3.16, os ficheiros que foram enviados deste dispositivo foram copiados para a pasta local %1$s para prevenir perda de dados quando um ficheiro está partilhado com várias contas.\nDevido a esta alteração, todos os ficheiros e as suas versões foram copiados para a pasta %2$s. Contudo, um erro não deixou concluír este processo durante a sincronização da conta. Pode deixar o ficheiro(s) como está(ão) e remover o link para %3$s, ou mover o(s) ficheiro(s)  para a pasta %1$s e guardar o link para %4$s.\n\nEm baixo está(ão) listados o(s) ficheiro(s) locais e remotos em %5$s que foram ligados.</string>
   <string name="sync_foreign_files_forgotten_explanation">Com a versão 1.3.16, os ficheiros que foram enviados deste dispositivo foram copiados para a pasta local %1$s para prevenir perda de dados quando um ficheiro está partilhado com várias contas.\nDevido a esta alteração, todos os ficheiros e as suas versões foram copiados para a pasta %2$s. Contudo, um erro não deixou concluír este processo durante a sincronização da conta. Pode deixar o ficheiro(s) como está(ão) e remover o link para %3$s, ou mover o(s) ficheiro(s)  para a pasta %1$s e guardar o link para %4$s.\n\nEm baixo está(ão) listados o(s) ficheiro(s) locais e remotos em %5$s que foram ligados.</string>
+  <string name="sync_current_folder_was_removed">A pasta %1$s já não existe</string>
   <string name="foreign_files_move">Mover Todos</string>
   <string name="foreign_files_move">Mover Todos</string>
   <string name="foreign_files_success">Todos os ficheiros foram movidos</string>
   <string name="foreign_files_success">Todos os ficheiros foram movidos</string>
   <string name="foreign_files_fail">Não foi possível mover alguns ficheiros</string>
   <string name="foreign_files_fail">Não foi possível mover alguns ficheiros</string>
@@ -117,6 +121,7 @@
   <string name="media_err_unsupported">Codec de média não suportado</string>
   <string name="media_err_unsupported">Codec de média não suportado</string>
   <string name="media_err_io">Não foi possível reproduzir o ficheiro</string>
   <string name="media_err_io">Não foi possível reproduzir o ficheiro</string>
   <string name="media_err_malformed">Ficheiro erradamente codificado (codec)</string>
   <string name="media_err_malformed">Ficheiro erradamente codificado (codec)</string>
+  <string name="media_err_timeout">O tempo de espera para jogar expirou</string>
   <string name="media_err_invalid_progressive_playback">O ficheiro não pode ser reproduzido (streaming)</string>
   <string name="media_err_invalid_progressive_playback">O ficheiro não pode ser reproduzido (streaming)</string>
   <string name="media_err_unknown">O ficheiro não pode ser reproduzido com o leitor de média de origem</string>
   <string name="media_err_unknown">O ficheiro não pode ser reproduzido com o leitor de média de origem</string>
   <string name="media_err_security_ex">Erro de segurança a tentar reproduzir o ficheiro %1$s</string>
   <string name="media_err_security_ex">Erro de segurança a tentar reproduzir o ficheiro %1$s</string>
@@ -131,12 +136,15 @@
   <string name="auth_connection_established">Ligação estabelecida</string>
   <string name="auth_connection_established">Ligação estabelecida</string>
   <string name="auth_testing_connection">A testar a ligação...</string>
   <string name="auth_testing_connection">A testar a ligação...</string>
   <string name="auth_not_configured_title">Configuração do servidor incorrecta.</string>
   <string name="auth_not_configured_title">Configuração do servidor incorrecta.</string>
+  <string name="auth_account_not_new">Uma conta para este utilizador e servidor já existe no dispositivo</string>
+  <string name="auth_account_not_the_same">O utilizador que escreveu não coincide com o nome de utilizador desta conta</string>
   <string name="auth_unknown_error_title">Ocorreu um erro desconhecido!</string>
   <string name="auth_unknown_error_title">Ocorreu um erro desconhecido!</string>
   <string name="auth_unknown_host_title">Não é possível encontrar o servidor</string>
   <string name="auth_unknown_host_title">Não é possível encontrar o servidor</string>
   <string name="auth_incorrect_path_title">Instância servidor não encontrada</string>
   <string name="auth_incorrect_path_title">Instância servidor não encontrada</string>
   <string name="auth_timeout_title">O servidor levou demasiado tempo a responder</string>
   <string name="auth_timeout_title">O servidor levou demasiado tempo a responder</string>
   <string name="auth_incorrect_address_title">URL errado</string>
   <string name="auth_incorrect_address_title">URL errado</string>
   <string name="auth_ssl_general_error_title">Inicialização de SSL falhou</string>
   <string name="auth_ssl_general_error_title">Inicialização de SSL falhou</string>
+  <string name="auth_ssl_unverified_server_title">Não foi possível verificar a identidade SSL do servidor</string>
   <string name="auth_bad_oc_version_title">Versão do servidor não reconhecida</string>
   <string name="auth_bad_oc_version_title">Versão do servidor não reconhecida</string>
   <string name="auth_wrong_connection_title">Não consegue estabelecer ligação</string>
   <string name="auth_wrong_connection_title">Não consegue estabelecer ligação</string>
   <string name="auth_secure_connection">Ligação segura estabelecida</string>
   <string name="auth_secure_connection">Ligação segura estabelecida</string>
@@ -144,7 +152,12 @@
   <string name="auth_oauth_error">autorização mal sucedida</string>
   <string name="auth_oauth_error">autorização mal sucedida</string>
   <string name="auth_oauth_error_access_denied">Acesso negado pelo servidor</string>
   <string name="auth_oauth_error_access_denied">Acesso negado pelo servidor</string>
   <string name="auth_wtf_reenter_URL">Estado inesperado, por favor, digite a URL do servidor novamente</string>
   <string name="auth_wtf_reenter_URL">Estado inesperado, por favor, digite a URL do servidor novamente</string>
+  <string name="auth_expired_oauth_token_toast">O prazo da sua autorização expirou. Por favor renove-a</string>
   <string name="auth_expired_basic_auth_toast">Por favor, introduza a password actual</string>
   <string name="auth_expired_basic_auth_toast">Por favor, introduza a password actual</string>
+  <string name="auth_expired_saml_sso_token_toast">A sua sessão expirou. Por favor autentique-se de novo</string>
+  <string name="auth_connecting_auth_server">A verificar a sua autenticação no servidor...</string>
+  <string name="auth_unsupported_auth_method">O servidor não suporta este método de autenticação</string>
+  <string name="auth_unsupported_multiaccount">%1$s não suporta contas múltiplas</string>
   <string name="fd_keep_in_sync">manter ficheiro actualizado</string>
   <string name="fd_keep_in_sync">manter ficheiro actualizado</string>
   <string name="common_rename">Renomear</string>
   <string name="common_rename">Renomear</string>
   <string name="common_remove">Remover</string>
   <string name="common_remove">Remover</string>
@@ -162,9 +175,11 @@
   <string name="sync_file_fail_msg">Não foi possível verificar o ficheiro remoto</string>
   <string name="sync_file_fail_msg">Não foi possível verificar o ficheiro remoto</string>
   <string name="sync_file_nothing_to_do_msg">O conteúdo do ficheiro já foi sincronizado</string>
   <string name="sync_file_nothing_to_do_msg">O conteúdo do ficheiro já foi sincronizado</string>
   <string name="create_dir_fail_msg">Não foi possível criar a pasta</string>
   <string name="create_dir_fail_msg">Não foi possível criar a pasta</string>
+  <string name="filename_forbidden_characters">Caracteres não permitidos: / \\ &lt; &gt; : \" | ? *</string>
   <string name="wait_a_moment">Aguarde um momento</string>
   <string name="wait_a_moment">Aguarde um momento</string>
   <string name="filedisplay_unexpected_bad_get_content">Erro inesperado. Por favor tente outra aplicação para seleccionar o ficheiro.</string>
   <string name="filedisplay_unexpected_bad_get_content">Erro inesperado. Por favor tente outra aplicação para seleccionar o ficheiro.</string>
   <string name="filedisplay_no_file_selected">Não selecionou nenhum ficheiro</string>
   <string name="filedisplay_no_file_selected">Não selecionou nenhum ficheiro</string>
+  <string name="oauth_check_onoff">Autenticar-se com oAuth2</string>
   <string name="oauth_login_connection">A ligar ao servidor oAuth2</string>
   <string name="oauth_login_connection">A ligar ao servidor oAuth2</string>
   <string name="ssl_validator_header">Não foi possível verificar a identidade do site.</string>
   <string name="ssl_validator_header">Não foi possível verificar a identidade do site.</string>
   <string name="ssl_validator_reason_cert_not_trusted">- O certificado do servidor não é de confiança</string>
   <string name="ssl_validator_reason_cert_not_trusted">- O certificado do servidor não é de confiança</string>
@@ -204,6 +219,7 @@
   <string name="preview_image_description">Pré-Visualização da imagem</string>
   <string name="preview_image_description">Pré-Visualização da imagem</string>
   <string name="preview_image_error_unknown_format">Esta imagem não pode ser mostrada</string>
   <string name="preview_image_error_unknown_format">Esta imagem não pode ser mostrada</string>
   <string name="error__upload__local_file_not_copied">Não foi possível copiar %1$s para a pasta local %2$s</string>
   <string name="error__upload__local_file_not_copied">Não foi possível copiar %1$s para a pasta local %2$s</string>
+  <string name="actionbar_failed_instant_upload">O envio rápido falhou</string>
   <string name="failed_upload_headline_text">Falharam os Uploads-Instantâneos</string>
   <string name="failed_upload_headline_text">Falharam os Uploads-Instantâneos</string>
   <string name="failed_upload_headline_hint">Sumário dos Uploads-Instantâneos falhados</string>
   <string name="failed_upload_headline_hint">Sumário dos Uploads-Instantâneos falhados</string>
   <string name="failed_upload_all_cb">Seleccionar Todos</string>
   <string name="failed_upload_all_cb">Seleccionar Todos</string>

+ 5 - 0
res/values-sk-rSK/strings.xml

@@ -28,6 +28,7 @@
   <string name="prefs_feedback">Spätná väzba</string>
   <string name="prefs_feedback">Spätná väzba</string>
   <string name="prefs_imprint">Podmienky používania</string>
   <string name="prefs_imprint">Podmienky používania</string>
   <string name="recommend_subject">Skúste %1$s na vašom telefóne!</string>
   <string name="recommend_subject">Skúste %1$s na vašom telefóne!</string>
+  <string name="recommend_text">Chcem vás pozvať na používanie %1$s na vašom smartphone!\nNa stiahnutie tu: %2$s</string>
   <string name="auth_check_server">Skontrolovať Server</string>
   <string name="auth_check_server">Skontrolovať Server</string>
   <string name="auth_host_url">Adresa servera</string>
   <string name="auth_host_url">Adresa servera</string>
   <string name="auth_username">Používateľské meno</string>
   <string name="auth_username">Používateľské meno</string>
@@ -94,6 +95,7 @@
   <string name="sync_foreign_files_forgotten_ticker">Niektoré lokálne súbory boli zabudnuté</string>
   <string name="sync_foreign_files_forgotten_ticker">Niektoré lokálne súbory boli zabudnuté</string>
   <string name="sync_foreign_files_forgotten_content">%1$d súborov z priečinka %2$s nemožno skopírovať do</string>
   <string name="sync_foreign_files_forgotten_content">%1$d súborov z priečinka %2$s nemožno skopírovať do</string>
   <string name="sync_foreign_files_forgotten_explanation">Od verzie 1.3.16 sú súbory nahrané z tohoto zariadenia, skopírovné do lokálneho priečinka %1$s, aby sa zabránilo strate dát, keď je jeden súbor synchronizovný s viacerými účtami.\n\nPre túto zmenu, všetky súbory nahraté v predchádzajúcich verziách tejto aplikácie boli skopírované do priečinka %2$s. Je nám to ľúto, chyba zabránila dokončeniu tejto operácie počas synchronizácie účtu. Súbor(y) môžete ponechať v súčasnom stave a zmazať odkaz na %3$s, alebo presunúť súbor(y) do priečinka %1$s a zachovať odkaz na %4$s.\n\nĎalej lokálny súbor(y) a vzdialený súbor(y) sú spojené v priečinku %5$s.</string>
   <string name="sync_foreign_files_forgotten_explanation">Od verzie 1.3.16 sú súbory nahrané z tohoto zariadenia, skopírovné do lokálneho priečinka %1$s, aby sa zabránilo strate dát, keď je jeden súbor synchronizovný s viacerými účtami.\n\nPre túto zmenu, všetky súbory nahraté v predchádzajúcich verziách tejto aplikácie boli skopírované do priečinka %2$s. Je nám to ľúto, chyba zabránila dokončeniu tejto operácie počas synchronizácie účtu. Súbor(y) môžete ponechať v súčasnom stave a zmazať odkaz na %3$s, alebo presunúť súbor(y) do priečinka %1$s a zachovať odkaz na %4$s.\n\nĎalej lokálny súbor(y) a vzdialený súbor(y) sú spojené v priečinku %5$s.</string>
+  <string name="sync_current_folder_was_removed">Priečinok %1$s už existuje</string>
   <string name="foreign_files_move">Premiestniť všetko</string>
   <string name="foreign_files_move">Premiestniť všetko</string>
   <string name="foreign_files_success">Všetky súbory boli premiestnené</string>
   <string name="foreign_files_success">Všetky súbory boli premiestnené</string>
   <string name="foreign_files_fail">Niektoré súbory nebolo možné premiestniť</string>
   <string name="foreign_files_fail">Niektoré súbory nebolo možné premiestniť</string>
@@ -173,9 +175,11 @@
   <string name="sync_file_fail_msg">Vzdialený súbor nemohol byť prekontrolovaný</string>
   <string name="sync_file_fail_msg">Vzdialený súbor nemohol byť prekontrolovaný</string>
   <string name="sync_file_nothing_to_do_msg">Obsah súboru je zosynchronizovaný</string>
   <string name="sync_file_nothing_to_do_msg">Obsah súboru je zosynchronizovaný</string>
   <string name="create_dir_fail_msg">Priečinok nie je možné vytvoriť</string>
   <string name="create_dir_fail_msg">Priečinok nie je možné vytvoriť</string>
+  <string name="filename_forbidden_characters">Zakázané znaky: / \\ &lt; &gt; : \" | ? *</string>
   <string name="wait_a_moment">Počkať chvíľu</string>
   <string name="wait_a_moment">Počkať chvíľu</string>
   <string name="filedisplay_unexpected_bad_get_content">Neočakávaný problém; skúste vybrať súbor inou aplikáciou</string>
   <string name="filedisplay_unexpected_bad_get_content">Neočakávaný problém; skúste vybrať súbor inou aplikáciou</string>
   <string name="filedisplay_no_file_selected">Nebol vybraný súbor</string>
   <string name="filedisplay_no_file_selected">Nebol vybraný súbor</string>
+  <string name="oauth_check_onoff">Prihlásiť sa z oAuth2</string>
   <string name="oauth_login_connection">Pripájam sa na oAuth2 server…</string>
   <string name="oauth_login_connection">Pripájam sa na oAuth2 server…</string>
   <string name="ssl_validator_header">Identitu stránky nemožno overiť</string>
   <string name="ssl_validator_header">Identitu stránky nemožno overiť</string>
   <string name="ssl_validator_reason_cert_not_trusted">- Certifikát servera nie je overený</string>
   <string name="ssl_validator_reason_cert_not_trusted">- Certifikát servera nie je overený</string>
@@ -215,6 +219,7 @@
   <string name="preview_image_description">Ukážka obrazu</string>
   <string name="preview_image_description">Ukážka obrazu</string>
   <string name="preview_image_error_unknown_format">Obraz nemôže byť zobrazený</string>
   <string name="preview_image_error_unknown_format">Obraz nemôže byť zobrazený</string>
   <string name="error__upload__local_file_not_copied">%1$s nemožno skopírovať do lokálneho priečinka %2$s</string>
   <string name="error__upload__local_file_not_copied">%1$s nemožno skopírovať do lokálneho priečinka %2$s</string>
+  <string name="actionbar_failed_instant_upload">Okamžité odoslanie zlyhalo</string>
   <string name="failed_upload_headline_text">Zlyhané instantné nahratia</string>
   <string name="failed_upload_headline_text">Zlyhané instantné nahratia</string>
   <string name="failed_upload_headline_hint">Zhrnutie všetkých zlyhaných nahratí</string>
   <string name="failed_upload_headline_hint">Zhrnutie všetkých zlyhaných nahratí</string>
   <string name="failed_upload_all_cb">vybrať všetko</string>
   <string name="failed_upload_all_cb">vybrať všetko</string>

+ 1 - 1
res/values-tr/strings.xml

@@ -93,7 +93,7 @@
   <string name="sync_fail_in_favourites_ticker">Korunan dosya senkronizasyonu başarısız</string>
   <string name="sync_fail_in_favourites_ticker">Korunan dosya senkronizasyonu başarısız</string>
   <string name="sync_fail_in_favourites_content">%1$d dosya  senkronize edilemedi  (%2$d hata)</string>
   <string name="sync_fail_in_favourites_content">%1$d dosya  senkronize edilemedi  (%2$d hata)</string>
   <string name="sync_foreign_files_forgotten_ticker">Bazı yerel dosyalar unutuldu</string>
   <string name="sync_foreign_files_forgotten_ticker">Bazı yerel dosyalar unutuldu</string>
-  <string name="sync_foreign_files_forgotten_content">%1$d dosyalar  %2$s dizinine kopyalanamadı</string>
+  <string name="sync_foreign_files_forgotten_content">%1$d dosya %2$s dizinine kopyalanamadı</string>
   <string name="sync_foreign_files_forgotten_explanation">1.3.16 sürümünden sonra, bu aygıttan yüklenen dosyalar bir dosya birden fazla hesapla eşitlendiğinde veri kaybının önlenebilmesi için %1$s yerel klasörüne kopyalanır.\n\nBu değişiklikten dolayı, bu uygulamanın yüklenmiş tüm önceki sürümündeki dosyalar %2$s klasörüne kopyalandı. Ancak hesap eşitlenmesi sırasında bu işlemin tamamlanmasını engelleyen bir hata oluştu. Dosyayı/dosyaları olduğu gibi bırakabilir ve %3$s bağlantısını kaldırabilirsiniz veya dosyayı/dosyaları %1$s dizinine taşıyıp %4$s bağlantılarını koruyabilirsiniz.\n\nAşağıda listelenenler yerel dosyalar ve bağlı oldukları %5$s içerisindeki uzak dosyalardır.</string>
   <string name="sync_foreign_files_forgotten_explanation">1.3.16 sürümünden sonra, bu aygıttan yüklenen dosyalar bir dosya birden fazla hesapla eşitlendiğinde veri kaybının önlenebilmesi için %1$s yerel klasörüne kopyalanır.\n\nBu değişiklikten dolayı, bu uygulamanın yüklenmiş tüm önceki sürümündeki dosyalar %2$s klasörüne kopyalandı. Ancak hesap eşitlenmesi sırasında bu işlemin tamamlanmasını engelleyen bir hata oluştu. Dosyayı/dosyaları olduğu gibi bırakabilir ve %3$s bağlantısını kaldırabilirsiniz veya dosyayı/dosyaları %1$s dizinine taşıyıp %4$s bağlantılarını koruyabilirsiniz.\n\nAşağıda listelenenler yerel dosyalar ve bağlı oldukları %5$s içerisindeki uzak dosyalardır.</string>
   <string name="sync_current_folder_was_removed">%1$s klasörü artık mevcut değil.</string>
   <string name="sync_current_folder_was_removed">%1$s klasörü artık mevcut değil.</string>
   <string name="foreign_files_move">Tümünü taşı</string>
   <string name="foreign_files_move">Tümünü taşı</string>

+ 1 - 1
res/values/strings.xml

@@ -34,7 +34,7 @@
 	<string name="recommend_text">"I want to invite you to use %1$s on your smartphone!\nDownload here: %2$s"</string>  
 	<string name="recommend_text">"I want to invite you to use %1$s on your smartphone!\nDownload here: %2$s"</string>  
 
 
     <string name="auth_check_server">Check Server</string>
     <string name="auth_check_server">Check Server</string>
-    <string name="auth_host_url">Server address</string>
+    <string name="auth_host_url">Server address https://…</string>
     <string name="auth_username">Username</string>
     <string name="auth_username">Username</string>
     <string name="auth_password">Password</string>
     <string name="auth_password">Password</string>
     <string name="auth_register">New to %1$s?</string>
     <string name="auth_register">New to %1$s?</string>

+ 3 - 2
src/com/owncloud/android/files/managers/OCNotificationManager.java

@@ -22,6 +22,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Map;
 
 
 import com.owncloud.android.R;
 import com.owncloud.android.R;
+import com.owncloud.android.utils.DisplayUtils;
 
 
 import android.app.Notification;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.NotificationManager;
@@ -101,7 +102,7 @@ public class OCNotificationManager {
         
         
         switch (type) {
         switch (type) {
             case NOTIFICATION_SIMPLE:
             case NOTIFICATION_SIMPLE:
-                notification = new Notification(R.drawable.icon, data.getText(), System.currentTimeMillis());
+                notification = new Notification(DisplayUtils.getSeasonalIconId(), data.getText(), System.currentTimeMillis());
                 break;
                 break;
             case NOTIFICATION_PROGRESS:
             case NOTIFICATION_PROGRESS:
                 notification = new Notification();
                 notification = new Notification();
@@ -139,7 +140,7 @@ public class OCNotificationManager {
                                                               false);
                                                               false);
                 return true;
                 return true;
             case NOTIFICATION_SIMPLE:
             case NOTIFICATION_SIMPLE:
-                pair.mNotificaiton = new Notification(R.drawable.icon,
+                pair.mNotificaiton = new Notification(DisplayUtils.getSeasonalIconId(),
                                                       data.getText(), System.currentTimeMillis());
                                                       data.getText(), System.currentTimeMillis());
                 mNM.notify(notification_id, pair.mNotificaiton);
                 mNM.notify(notification_id, pair.mNotificaiton);
                 return true;
                 return true;

+ 4 - 3
src/com/owncloud/android/files/services/FileDownloader.java

@@ -43,6 +43,7 @@ import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
 import com.owncloud.android.ui.preview.PreviewImageFragment;
 import com.owncloud.android.ui.preview.PreviewImageFragment;
+import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.Log_OC;
 import com.owncloud.android.utils.Log_OC;
 
 
 import android.accounts.Account;
 import android.accounts.Account;
@@ -409,12 +410,12 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     private void notifyDownloadStart(DownloadFileOperation download) {
     private void notifyDownloadStart(DownloadFileOperation download) {
         /// create status notification with a progress bar
         /// create status notification with a progress bar
         mLastPercent = 0;
         mLastPercent = 0;
-        mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());
+        mNotification = new Notification(DisplayUtils.getSeasonalIconId(), getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());
         mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
         mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
         mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);
         mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);
         mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() < 0);
         mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() < 0);
         mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getSavePath()).getName()));
         mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getSavePath()).getName()));
-        mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
+        mNotification.contentView.setImageViewResource(R.id.status_icon, DisplayUtils.getSeasonalIconId());
         
         
         /// includes a pending intent in the notification showing the details view of the file
         /// includes a pending intent in the notification showing the details view of the file
         Intent showDetailsIntent = null;
         Intent showDetailsIntent = null;
@@ -468,7 +469,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         if (!downloadResult.isCancelled()) {
         if (!downloadResult.isCancelled()) {
             int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;
             int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;
             int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content;
             int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content;
-            Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis());
+            Notification finalNotification = new Notification(DisplayUtils.getSeasonalIconId(), getString(tickerId), System.currentTimeMillis());
             finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
             finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
             boolean needsToUpdateCredentials = (downloadResult.getCode() == ResultCode.UNAUTHORIZED ||
             boolean needsToUpdateCredentials = (downloadResult.getCode() == ResultCode.UNAUTHORIZED ||
                                                 // (downloadResult.isTemporalRedirection() && downloadResult.isIdPRedirection()
                                                 // (downloadResult.isTemporalRedirection() && downloadResult.isIdPRedirection()

+ 8 - 4
src/com/owncloud/android/files/services/FileUploader.java

@@ -48,6 +48,7 @@ import com.owncloud.android.oc_framework.operations.RemoteOperationResult.Result
 import com.owncloud.android.oc_framework.utils.OwnCloudVersion;
 import com.owncloud.android.oc_framework.utils.OwnCloudVersion;
 import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener;
 import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener;
 import com.owncloud.android.oc_framework.accounts.OwnCloudAccount;
 import com.owncloud.android.oc_framework.accounts.OwnCloudAccount;
+import com.owncloud.android.oc_framework.network.webdav.ChunkFromFileChannelRequestEntity;
 import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory;
 import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory;
 import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
 import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
 import com.owncloud.android.oc_framework.network.webdav.WebdavEntry;
 import com.owncloud.android.oc_framework.network.webdav.WebdavEntry;
@@ -58,6 +59,7 @@ import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.activity.InstantUploadActivity;
 import com.owncloud.android.ui.activity.InstantUploadActivity;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
 import com.owncloud.android.ui.preview.PreviewImageFragment;
 import com.owncloud.android.ui.preview.PreviewImageFragment;
+import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.Log_OC;
 import com.owncloud.android.utils.Log_OC;
 
 
 import android.accounts.Account;
 import android.accounts.Account;
@@ -265,7 +267,9 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         try {
         try {
             for (int i = 0; i < files.length; i++) {
             for (int i = 0; i < files.length; i++) {
                 uploadKey = buildRemoteName(account, files[i].getRemotePath());
                 uploadKey = buildRemoteName(account, files[i].getRemotePath());
-                if (chunked) {
+                if (chunked
+                        && (new File(files[i].getStoragePath())).length() > ChunkedUploadFileOperation.CHUNK_SIZE)  // added to work around bug in servers 5.x 
+                {
                     newUpload = new ChunkedUploadFileOperation(account, files[i], isInstant, forceOverwrite,
                     newUpload = new ChunkedUploadFileOperation(account, files[i], isInstant, forceOverwrite,
                             localAction);
                             localAction);
                 } else {
                 } else {
@@ -714,7 +718,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     private void notifyUploadStart(UploadFileOperation upload) {
     private void notifyUploadStart(UploadFileOperation upload) {
         // / create status notification with a progress bar
         // / create status notification with a progress bar
         mLastPercent = 0;
         mLastPercent = 0;
-        mNotification = new Notification(R.drawable.icon, getString(R.string.uploader_upload_in_progress_ticker),
+        mNotification = new Notification(DisplayUtils.getSeasonalIconId(), getString(R.string.uploader_upload_in_progress_ticker),
                 System.currentTimeMillis());
                 System.currentTimeMillis());
         mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
         mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
         mDefaultNotificationContentView = mNotification.contentView;
         mDefaultNotificationContentView = mNotification.contentView;
@@ -723,7 +727,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, false);
         mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, false);
         mNotification.contentView.setTextViewText(R.id.status_text,
         mNotification.contentView.setTextViewText(R.id.status_text,
                 String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName()));
                 String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName()));
-        mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
+        mNotification.contentView.setImageViewResource(R.id.status_icon, DisplayUtils.getSeasonalIconId());
         
         
         /// includes a pending intent in the notification showing the details view of the file
         /// includes a pending intent in the notification showing the details view of the file
         Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
         Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
@@ -810,7 +814,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
 
             // / fail -> explicit failure notification
             // / fail -> explicit failure notification
             mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
             mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
-            Notification finalNotification = new Notification(R.drawable.icon,
+            Notification finalNotification = new Notification(DisplayUtils.getSeasonalIconId(),
                     getString(R.string.uploader_upload_failed_ticker), System.currentTimeMillis());
                     getString(R.string.uploader_upload_failed_ticker), System.currentTimeMillis());
             finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
             finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
             String content = null;
             String content = null;

+ 1 - 1
src/com/owncloud/android/operations/ChunkedUploadFileOperation.java

@@ -40,7 +40,7 @@ import android.accounts.Account;
 
 
 public class ChunkedUploadFileOperation extends UploadFileOperation {
 public class ChunkedUploadFileOperation extends UploadFileOperation {
     
     
-    private static final long CHUNK_SIZE = 1024000;
+    public static final long CHUNK_SIZE = 1024000;
     private static final String OC_CHUNKED_HEADER = "OC-Chunked";
     private static final String OC_CHUNKED_HEADER = "OC-Chunked";
     private static final String TAG = ChunkedUploadFileOperation.class.getSimpleName();
     private static final String TAG = ChunkedUploadFileOperation.class.getSimpleName();
 
 

+ 5 - 4
src/com/owncloud/android/syncadapter/FileSyncAdapter.java

@@ -36,6 +36,7 @@ import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.UpdateOCVersionOperation;
 import com.owncloud.android.operations.UpdateOCVersionOperation;
 import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;
 import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;
+import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.Log_OC;
 import com.owncloud.android.utils.Log_OC;
 
 
 
 
@@ -368,7 +369,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
      * Notifies the user about a failed synchronization through the status notification bar 
      * Notifies the user about a failed synchronization through the status notification bar 
      */
      */
     private void notifyFailedSynchronization() {
     private void notifyFailedSynchronization() {
-        Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());
+        Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());
         notification.flags |= Notification.FLAG_AUTO_CANCEL;
         notification.flags |= Notification.FLAG_AUTO_CANCEL;
         boolean needsToUpdateCredentials = (mLastFailedResult != null && 
         boolean needsToUpdateCredentials = (mLastFailedResult != null && 
                                              (  mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED ||
                                              (  mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED ||
@@ -410,7 +411,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
      */
      */
     private void notifyFailsInFavourites() {
     private void notifyFailsInFavourites() {
         if (mFailedResultsCounter > 0) {
         if (mFailedResultsCounter > 0) {
-            Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis());
+            Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis());
             notification.flags |= Notification.FLAG_AUTO_CANCEL;
             notification.flags |= Notification.FLAG_AUTO_CANCEL;
             // TODO put something smart in the contentIntent below
             // TODO put something smart in the contentIntent below
             notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
             notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
@@ -421,7 +422,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
             ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_in_favourites_ticker, notification);
             ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_in_favourites_ticker, notification);
             
             
         } else {
         } else {
-            Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis());
+            Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis());
             notification.flags |= Notification.FLAG_AUTO_CANCEL;
             notification.flags |= Notification.FLAG_AUTO_CANCEL;
             // TODO put something smart in the contentIntent below
             // TODO put something smart in the contentIntent below
             notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
             notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
@@ -443,7 +444,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
      * We won't consider a synchronization as failed when foreign files can not be copied to the ownCloud local directory.
      * We won't consider a synchronization as failed when foreign files can not be copied to the ownCloud local directory.
      */
      */
     private void notifyForgottenLocalFiles() {
     private void notifyForgottenLocalFiles() {
-        Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_foreign_files_forgotten_ticker), System.currentTimeMillis());
+        Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_foreign_files_forgotten_ticker), System.currentTimeMillis());
         notification.flags |= Notification.FLAG_AUTO_CANCEL;
         notification.flags |= Notification.FLAG_AUTO_CANCEL;
 
 
         /// includes a pending intent in the notification showing a more detailed explanation
         /// includes a pending intent in the notification showing a more detailed explanation

+ 5 - 4
src/com/owncloud/android/ui/activity/AccountSelectActivity.java

@@ -27,7 +27,6 @@ import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerCallback;
 import android.accounts.AccountManagerCallback;
 import android.accounts.AccountManagerFuture;
 import android.accounts.AccountManagerFuture;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Context;
 import android.content.Intent;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Bundle;
@@ -50,6 +49,7 @@ import com.actionbarsherlock.view.MenuItem;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.oc_framework.accounts.OwnCloudAccount;
 import com.owncloud.android.oc_framework.accounts.OwnCloudAccount;
+import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.Log_OC;
 import com.owncloud.android.utils.Log_OC;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
@@ -75,9 +75,10 @@ public class AccountSelectActivity extends SherlockListActivity implements
             mPreviousAccount = AccountUtils.getCurrentOwnCloudAccount(this);
             mPreviousAccount = AccountUtils.getCurrentOwnCloudAccount(this);
         }
         }
         
         
-        ActionBar action_bar = getSupportActionBar();
-        action_bar.setDisplayShowTitleEnabled(true);
-        action_bar.setDisplayHomeAsUpEnabled(false);
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setIcon(DisplayUtils.getSeasonalIconId());
+        actionBar.setDisplayShowTitleEnabled(true);
+        actionBar.setDisplayHomeAsUpEnabled(false);
     }
     }
 
 
     @Override
     @Override

+ 5 - 1
src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java

@@ -18,12 +18,14 @@
 
 
 package com.owncloud.android.ui.activity;
 package com.owncloud.android.ui.activity;
 
 
+import com.actionbarsherlock.app.ActionBar;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
 import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
 import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
 import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
 import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
 import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
+import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.Log_OC;
 import com.owncloud.android.utils.Log_OC;
 
 
 import android.content.Intent;
 import android.content.Intent;
@@ -43,10 +45,12 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
     @Override
     @Override
     protected void onCreate(Bundle savedInstanceState) {
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         super.onCreate(savedInstanceState);
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setIcon(DisplayUtils.getSeasonalIconId());
     }
     }
 
 
     @Override
     @Override
-    public void ConflictDecisionMade(Decision decision) {
+    public void conflictDecisionMade(Decision decision) {
         Intent i = new Intent(getApplicationContext(), FileUploader.class);
         Intent i = new Intent(getApplicationContext(), FileUploader.class);
         
         
         switch (decision) {
         switch (decision) {

+ 8 - 17
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -88,6 +88,7 @@ import com.owncloud.android.ui.fragment.OCFileListFragment;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
 import com.owncloud.android.ui.preview.PreviewMediaFragment;
 import com.owncloud.android.ui.preview.PreviewMediaFragment;
 import com.owncloud.android.ui.preview.PreviewVideoActivity;
 import com.owncloud.android.ui.preview.PreviewVideoActivity;
+import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.Log_OC;
 import com.owncloud.android.utils.Log_OC;
 
 
 
 
@@ -192,13 +193,16 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
         // Action bar setup
         // Action bar setup
         mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
         mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
         getSupportActionBar().setHomeButtonEnabled(true);       // mandatory since Android ICS, according to the official documentation
         getSupportActionBar().setHomeButtonEnabled(true);       // mandatory since Android ICS, according to the official documentation
-        setSupportProgressBarIndeterminateVisibility(mSyncInProgress);    // always AFTER setContentView(...) ; to work around bug in its implementation        
-        
-        
+        setSupportProgressBarIndeterminateVisibility(mSyncInProgress);    // always AFTER setContentView(...) ; to work around bug in its implementation
         
         
         Log_OC.d(TAG, "onCreate() end");
         Log_OC.d(TAG, "onCreate() end");
     }
     }
-
+    
+    @Override
+    protected void onStart() {
+        super.onStart();
+        getSupportActionBar().setIcon(DisplayUtils.getSeasonalIconId());
+    }
 
 
     @Override
     @Override
     protected void onDestroy() {
     protected void onDestroy() {
@@ -1151,19 +1155,6 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
     }
     }
 
 
 
 
-//    private void updateDisplayHomeAtSync(){
-//        ActionBar actionBar = getSupportActionBar();
-//        OCFile currentDir = getCurrentDir();
-//        if (currentDir.getParentId() != DataStorageManager.ROOT_PARENT_ID) {
-//            actionBar.setHomeButtonEnabled(!mSyncInProgress);
-//            actionBar.setDisplayHomeAsUpEnabled(!mSyncInProgress);
-//        }
-//        else {
-//            actionBar.setHomeButtonEnabled(true);
-//            actionBar.setDisplayHomeAsUpEnabled(false);
-//        }
-//    }
-//    
     /**
     /**
      * {@inheritDoc}
      * {@inheritDoc}
      */
      */

+ 5 - 0
src/com/owncloud/android/ui/activity/GenericExplanationActivity.java

@@ -30,8 +30,10 @@ import android.widget.ListAdapter;
 import android.widget.ListView;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.TextView;
 
 
+import com.actionbarsherlock.app.ActionBar;
 import com.actionbarsherlock.app.SherlockFragmentActivity;
 import com.actionbarsherlock.app.SherlockFragmentActivity;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
+import com.owncloud.android.utils.DisplayUtils;
 
 
 
 
 /**
 /**
@@ -74,6 +76,9 @@ public class GenericExplanationActivity  extends SherlockFragmentActivity {
         } else {
         } else {
             listView.setVisibility(View.GONE);
             listView.setVisibility(View.GONE);
         }
         }
+        
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setIcon(DisplayUtils.getSeasonalIconId());
     }
     }
     
     
     public class ExplanationListAdapterView extends ArrayAdapter<String> {
     public class ExplanationListAdapterView extends ArrayAdapter<String> {

+ 2 - 0
src/com/owncloud/android/ui/activity/LogHistoryActivity.java

@@ -34,6 +34,7 @@ import com.actionbarsherlock.app.SherlockPreferenceActivity;
 import com.actionbarsherlock.view.MenuItem;
 import com.actionbarsherlock.view.MenuItem;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
 import com.owncloud.android.ui.adapter.LogListAdapter;
 import com.owncloud.android.ui.adapter.LogListAdapter;
+import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.FileStorageUtils;
 
 
 
 
@@ -50,6 +51,7 @@ public class LogHistoryActivity extends SherlockPreferenceActivity implements On
         setContentView(R.layout.log_send_file);
         setContentView(R.layout.log_send_file);
         setTitle("Log History");
         setTitle("Log History");
         ActionBar actionBar = getSherlock().getActionBar();
         ActionBar actionBar = getSherlock().getActionBar();
+        actionBar.setIcon(DisplayUtils.getSeasonalIconId());
         actionBar.setDisplayHomeAsUpEnabled(true);
         actionBar.setDisplayHomeAsUpEnabled(true);
         ListView listView = (ListView) findViewById(android.R.id.list);
         ListView listView = (ListView) findViewById(android.R.id.list);
         Button deleteHistoryButton = (Button) findViewById(R.id.deleteLogHistoryButton);
         Button deleteHistoryButton = (Button) findViewById(R.id.deleteLogHistoryButton);

+ 4 - 1
src/com/owncloud/android/ui/activity/PinCodeActivity.java

@@ -18,8 +18,10 @@ package com.owncloud.android.ui.activity;
 
 
 import java.util.Arrays;
 import java.util.Arrays;
 
 
+import com.actionbarsherlock.app.ActionBar;
 import com.actionbarsherlock.app.SherlockFragmentActivity;
 import com.actionbarsherlock.app.SherlockFragmentActivity;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
+import com.owncloud.android.utils.DisplayUtils;
 
 
 import android.app.AlertDialog;
 import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.DialogInterface;
@@ -121,7 +123,8 @@ public class PinCodeActivity extends SherlockFragmentActivity {
         }
         }
         setTextListeners();
         setTextListeners();
         
         
-        
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setIcon(DisplayUtils.getSeasonalIconId());
     }
     }
     
     
 
 

+ 2 - 8
src/com/owncloud/android/ui/activity/Preferences.java

@@ -17,8 +17,6 @@
  */
  */
 package com.owncloud.android.ui.activity;
 package com.owncloud.android.ui.activity;
 
 
-import java.util.Vector;
-
 import android.content.Intent;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfo;
@@ -38,8 +36,8 @@ import com.actionbarsherlock.view.Menu;
 import com.actionbarsherlock.view.MenuItem;
 import com.actionbarsherlock.view.MenuItem;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
 import com.owncloud.android.db.DbHandler;
 import com.owncloud.android.db.DbHandler;
+import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.Log_OC;
 import com.owncloud.android.utils.Log_OC;
-import com.owncloud.android.utils.OwnCloudSession;
 
 
 
 
 /**
 /**
@@ -51,15 +49,11 @@ import com.owncloud.android.utils.OwnCloudSession;
 public class Preferences extends SherlockPreferenceActivity {
 public class Preferences extends SherlockPreferenceActivity {
     
     
     private static final String TAG = "OwnCloudPreferences";
     private static final String TAG = "OwnCloudPreferences";
-    private final int mNewSession = 47;
-    private final int mEditSession = 48;
     private DbHandler mDbHandler;
     private DbHandler mDbHandler;
-    private Vector<OwnCloudSession> mSessions;
     private CheckBoxPreference pCode;
     private CheckBoxPreference pCode;
     //private CheckBoxPreference pLogging;
     //private CheckBoxPreference pLogging;
     //private Preference pLoggingHistory;
     //private Preference pLoggingHistory;
     private Preference pAboutApp;
     private Preference pAboutApp;
-    private int mSelectedMenuItem;
 
 
 
 
     @SuppressWarnings("deprecation")
     @SuppressWarnings("deprecation")
@@ -67,10 +61,10 @@ public class Preferences extends SherlockPreferenceActivity {
     public void onCreate(Bundle savedInstanceState) {
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         super.onCreate(savedInstanceState);
         mDbHandler = new DbHandler(getBaseContext());
         mDbHandler = new DbHandler(getBaseContext());
-        mSessions = new Vector<OwnCloudSession>();
         addPreferencesFromResource(R.xml.preferences);
         addPreferencesFromResource(R.xml.preferences);
         //populateAccountList();
         //populateAccountList();
         ActionBar actionBar = getSherlock().getActionBar();
         ActionBar actionBar = getSherlock().getActionBar();
+        actionBar.setIcon(DisplayUtils.getSeasonalIconId());
         actionBar.setDisplayHomeAsUpEnabled(true);
         actionBar.setDisplayHomeAsUpEnabled(true);
         
         
         Preference p = findPreference("manage_account");
         Preference p = findPreference("manage_account");

+ 2 - 0
src/com/owncloud/android/ui/activity/UploadFilesActivity.java

@@ -40,6 +40,7 @@ import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
 import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
 import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
 import com.owncloud.android.ui.fragment.LocalFileListFragment;
 import com.owncloud.android.ui.fragment.LocalFileListFragment;
 import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
 import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
+import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.Log_OC;
 import com.owncloud.android.utils.Log_OC;
 
 
@@ -111,6 +112,7 @@ public class UploadFilesActivity extends FileActivity implements
             
             
         // Action bar setup
         // Action bar setup
         ActionBar actionBar = getSupportActionBar();
         ActionBar actionBar = getSupportActionBar();
+        actionBar.setIcon(DisplayUtils.getSeasonalIconId());
         actionBar.setHomeButtonEnabled(true);   // mandatory since Android ICS, according to the official documentation
         actionBar.setHomeButtonEnabled(true);   // mandatory since Android ICS, according to the official documentation
         actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getName() != null);
         actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getName() != null);
         actionBar.setDisplayShowTitleEnabled(false);
         actionBar.setDisplayShowTitleEnabled(false);

+ 2 - 1
src/com/owncloud/android/ui/dialog/ChangelogDialog.java

@@ -25,6 +25,7 @@ import android.webkit.WebView;
 
 
 import com.actionbarsherlock.app.SherlockDialogFragment;
 import com.actionbarsherlock.app.SherlockDialogFragment;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
+import com.owncloud.android.utils.DisplayUtils;
 
 
 
 
 /**
 /**
@@ -63,7 +64,7 @@ public class ChangelogDialog extends SherlockDialogFragment {
         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
         
         
         Dialog dialog = builder.setView(webview)
         Dialog dialog = builder.setView(webview)
-                                .setIcon(R.drawable.icon)
+                                .setIcon(DisplayUtils.getSeasonalIconId())
                                 //.setTitle(R.string.whats_new)
                                 //.setTitle(R.string.whats_new)
                                 .setPositiveButton(R.string.common_ok,
                                 .setPositiveButton(R.string.common_ok,
                                         new DialogInterface.OnClickListener() {
                                         new DialogInterface.OnClickListener() {

+ 7 - 6
src/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java

@@ -28,6 +28,7 @@ import android.support.v4.app.FragmentTransaction;
 import com.actionbarsherlock.app.SherlockDialogFragment;
 import com.actionbarsherlock.app.SherlockDialogFragment;
 import com.actionbarsherlock.app.SherlockFragmentActivity;
 import com.actionbarsherlock.app.SherlockFragmentActivity;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
+import com.owncloud.android.utils.DisplayUtils;
 
 
 
 
 /**
 /**
@@ -59,7 +60,7 @@ public class ConflictsResolveDialog extends SherlockDialogFragment {
     public Dialog onCreateDialog(Bundle savedInstanceState) {
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         String remotepath = getArguments().getString("remotepath");
         String remotepath = getArguments().getString("remotepath");
         return new AlertDialog.Builder(getSherlockActivity())
         return new AlertDialog.Builder(getSherlockActivity())
-                   .setIcon(R.drawable.icon)
+                   .setIcon(DisplayUtils.getSeasonalIconId())
                    .setTitle(R.string.conflict_title)
                    .setTitle(R.string.conflict_title)
                    .setMessage(String.format(getString(R.string.conflict_message), remotepath))
                    .setMessage(String.format(getString(R.string.conflict_message), remotepath))
                    .setPositiveButton(R.string.conflict_overwrite,
                    .setPositiveButton(R.string.conflict_overwrite,
@@ -68,7 +69,7 @@ public class ConflictsResolveDialog extends SherlockDialogFragment {
                            @Override
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                            public void onClick(DialogInterface dialog, int which) {
                                if (mListener != null)
                                if (mListener != null)
-                                   mListener.ConflictDecisionMade(Decision.OVERWRITE);
+                                   mListener.conflictDecisionMade(Decision.OVERWRITE);
                            }
                            }
                        })
                        })
                    .setNeutralButton(R.string.conflict_keep_both,
                    .setNeutralButton(R.string.conflict_keep_both,
@@ -76,7 +77,7 @@ public class ConflictsResolveDialog extends SherlockDialogFragment {
                             @Override
                             @Override
                             public void onClick(DialogInterface dialog, int which) {
                             public void onClick(DialogInterface dialog, int which) {
                                 if (mListener != null)
                                 if (mListener != null)
-                                    mListener.ConflictDecisionMade(Decision.KEEP_BOTH);
+                                    mListener.conflictDecisionMade(Decision.KEEP_BOTH);
                             }
                             }
                         })
                         })
                    .setNegativeButton(R.string.conflict_dont_upload,
                    .setNegativeButton(R.string.conflict_dont_upload,
@@ -84,7 +85,7 @@ public class ConflictsResolveDialog extends SherlockDialogFragment {
                            @Override
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                            public void onClick(DialogInterface dialog, int which) {
                                if (mListener != null)
                                if (mListener != null)
-                                   mListener.ConflictDecisionMade(Decision.CANCEL);
+                                   mListener.conflictDecisionMade(Decision.CANCEL);
                            }
                            }
                    })
                    })
                    .create();
                    .create();
@@ -112,10 +113,10 @@ public class ConflictsResolveDialog extends SherlockDialogFragment {
     
     
     @Override
     @Override
     public void onCancel(DialogInterface dialog) {
     public void onCancel(DialogInterface dialog) {
-        mListener.ConflictDecisionMade(Decision.CANCEL);
+        mListener.conflictDecisionMade(Decision.CANCEL);
     }
     }
     
     
     public interface OnConflictDecisionMadeListener {
     public interface OnConflictDecisionMadeListener {
-        public void ConflictDecisionMade(Decision decision);
+        public void conflictDecisionMade(Decision decision);
     }
     }
 }
 }

+ 2 - 0
src/com/owncloud/android/ui/preview/PreviewImageActivity.java

@@ -47,6 +47,7 @@ import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.dialog.LoadingDialog;
 import com.owncloud.android.ui.dialog.LoadingDialog;
 import com.owncloud.android.ui.fragment.FileFragment;
 import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.Log_OC;
 import com.owncloud.android.utils.Log_OC;
 
 
 
 
@@ -90,6 +91,7 @@ public class PreviewImageActivity extends FileActivity implements FileFragment.C
         setContentView(R.layout.preview_image_activity);
         setContentView(R.layout.preview_image_activity);
         
         
         ActionBar actionBar = getSupportActionBar();
         ActionBar actionBar = getSupportActionBar();
+        actionBar.setIcon(DisplayUtils.getSeasonalIconId());
         actionBar.setDisplayHomeAsUpEnabled(true);
         actionBar.setDisplayHomeAsUpEnabled(true);
         actionBar.hide();
         actionBar.hide();
         
         

+ 10 - 1
src/com/owncloud/android/utils/DisplayUtils.java

@@ -19,13 +19,13 @@
 package com.owncloud.android.utils;
 package com.owncloud.android.utils;
 
 
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.Set;
 
 
 import com.owncloud.android.R;
 import com.owncloud.android.R;
-import com.owncloud.android.R.drawable;
 
 
 /**
 /**
  * A helper class for some string operations.
  * A helper class for some string operations.
@@ -190,4 +190,13 @@ public class DisplayUtils {
         Date date = new Date(milliseconds);
         Date date = new Date(milliseconds);
         return date.toLocaleString();
         return date.toLocaleString();
     }
     }
+    
+    
+    public static int getSeasonalIconId() {
+        if (Calendar.getInstance().get(Calendar.DAY_OF_YEAR) >= 354) {
+            return R.drawable.winter_holidays_icon;
+        } else {
+            return R.drawable.icon;
+        }
+    }
 }
 }

+ 0 - 5
third_party/transifex-client/.gitignore

@@ -1,5 +0,0 @@
-.tx
-*pyc
-*pyo
-*~
-*egg-info*

+ 0 - 21
third_party/transifex-client/DEVELOPMENT.rst

@@ -1,21 +0,0 @@
-Releasing
-=========
-
-To create a new release:
-
-1. Update local rep and update the version in ``setup.py``::
-
-    $ hg pull -u
-    $ vim setup.py
-
-2. Test::
-
-    $ python setup.py clean sdist
-    $ cd dist
-    $ tar zxf ...
-    $ cd transifex-client
-    ...test
-
-3. Package and upload on PyPI::
-
-    $ python setup.py clean sdist bdist_egg upload

+ 0 - 343
third_party/transifex-client/LICENSE

@@ -1,343 +0,0 @@
-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-		    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software
-    interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Program does not specify a
-version number of this License, you may choose any version ever
-published by the Free Software Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-			    NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-	    How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these
-terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    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 2 of the License, or
-  
-    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, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
-Public License instead of this License.
-

+ 0 - 6
third_party/transifex-client/MANIFEST.in

@@ -1,6 +0,0 @@
-include tx
-
-# Docs
-include LICENSE README.rst
-recursive-include docs *
-

+ 0 - 30
third_party/transifex-client/README.rst

@@ -1,30 +0,0 @@
-
-=============================
- Transifex Command-Line Tool
-=============================
-
-The Transifex Command-line Client is a command line tool that enables
-you to easily manage your translations within a project without the need
-of an elaborate UI system.
-
-You can use the command line client to easily create new resources, map
-locale files to translations and synchronize your Transifex project with
-your local repository and vice verca. Translators and localization
-managers can also use it to handle large volumes of translation files
-easily and without much hassle.
-
-Check the full documentation at
-http://help.transifex.com/user-guide/client/
-
-
-Installing
-==========
-
-You can install the latest version of transifex-client running ``pip
-install transifex-client`` or ``easy_install transifex-client``
-You can also install the `in-development version`_ of transifex-client
-with ``pip install transifex-client==dev`` or ``easy_install
-transifex-client==dev``.
-
-.. _in-development version: http://code.transifex.com/transifex-client/
-

+ 0 - 56
third_party/transifex-client/setup.py

@@ -1,56 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import os
-import glob
-from codecs import BOM
-
-from setuptools import setup, find_packages
-from setuptools.command.build_py import build_py as _build_py
-
-from txclib import get_version
-
-readme_file = open(u'README.rst')
-long_description = readme_file.read()
-readme_file.close()
-if long_description.startswith(BOM):
-    long_description = long_description.lstrip(BOM)
-long_description = long_description.decode('utf-8')
-
-package_data = {
-    '': ['LICENSE', 'README.rst'],
-}
-
-scripts = ['tx']
-
-install_requires = []
-try:
-    import json
-except ImportError:
-    install_requires.append('simplejson')
-
-setup(
-    name="transifex-client",
-    version=get_version(),
-    scripts=scripts,
-    description="A command line interface for Transifex",
-    long_description=long_description,
-    author="Transifex",
-    author_email="info@transifex.com",
-    url="https://www.transifex.com",
-    license="GPLv2",
-    dependency_links = [
-    ],
-    setup_requires = [
-    ],
-    install_requires = install_requires,
-    tests_require = ["mock", ],
-    data_files=[
-    ],
-    test_suite="tests",
-    zip_safe=False,
-    packages=['txclib', ],
-    include_package_data=True,
-    package_data = package_data,
-    keywords = ('translation', 'localization', 'internationalization',),
-)

+ 0 - 0
third_party/transifex-client/tests/__init__.py


+ 0 - 65
third_party/transifex-client/tests/test_processors.py

@@ -1,65 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-Unit tests for processor functions.
-"""
-
-import unittest
-from urlparse import urlparse
-from txclib.processors import hostname_tld_migration, hostname_ssl_migration
-
-
-class TestHostname(unittest.TestCase):
-    """Test for hostname processors."""
-
-    def test_tld_migration_needed(self):
-        """
-        Test the tld migration of Transifex, when needed.
-        """
-        hostnames = [
-            'http://transifex.net', 'http://www.transifex.net',
-            'https://fedora.transifex.net',
-        ]
-        for h in hostnames:
-            hostname = hostname_tld_migration(h)
-            self.assertTrue(hostname.endswith('com'))
-        orig_hostname = 'http://www.transifex.net/path/'
-        hostname = hostname_tld_migration(orig_hostname)
-        self.assertEqual(hostname, orig_hostname.replace('net', 'com', 1))
-
-    def test_tld_migration_needed(self):
-        """
-        Test that unneeded tld migrations are detected correctly.
-        """
-        hostnames = [
-            'https://www.transifex.com', 'http://fedora.transifex.com',
-            'http://www.example.net/path/'
-        ]
-        for h in hostnames:
-            hostname = hostname_tld_migration(h)
-            self.assertEqual(hostname, h)
-
-    def test_no_scheme_specified(self):
-        """
-        Test that, if no scheme has been specified, the https one will be used.
-        """
-        hostname = '//transifex.net'
-        hostname = hostname_ssl_migration(hostname)
-        self.assertTrue(hostname.startswith('https://'))
-
-    def test_http_replacement(self):
-        """Test the replacement of http with https."""
-        hostnames = [
-            'http://transifex.com', 'http://transifex.net/http/',
-            'http://www.transifex.com/path/'
-        ]
-        for h in hostnames:
-            hostname = hostname_ssl_migration(h)
-            self.assertEqual(hostname[:8], 'https://')
-            self.assertEqual(hostname[7:], h[6:])
-
-    def test_no_http_replacement_needed(self):
-        """Test that http will not be replaces with https, when not needed."""
-        for h in ['http://example.com', 'http://example.com/http/']:
-            hostname = hostname_ssl_migration(h)
-            self.assertEqual(hostname, hostname)

+ 0 - 531
third_party/transifex-client/tests/test_project.py

@@ -1,531 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-import unittest
-import contextlib
-import itertools
-try:
-    import json
-except ImportError:
-    import simplejson as json
-from mock import Mock, patch
-
-from txclib.project import Project
-from txclib.config import Flipdict
-
-
-class TestProject(unittest.TestCase):
-
-    def test_extract_fields(self):
-        """Test the functions that extract a field from a stats object."""
-        stats = {
-            'completed': '80%',
-            'last_update': '00:00',
-            'foo': 'bar',
-        }
-        self.assertEqual(
-            stats['completed'], '%s%%' % Project._extract_completed(stats)
-        )
-        self.assertEqual(stats['last_update'], Project._extract_updated(stats))
-
-    def test_specifying_resources(self):
-        """Test the various ways to specify resources in a project."""
-        p = Project(init=False)
-        resources = [
-            'proj1.res1',
-            'proj2.res2',
-            'transifex.txn',
-            'transifex.txo',
-        ]
-        with patch.object(p, 'get_resource_list') as mock:
-            mock.return_value = resources
-            cmd_args = [
-                'proj1.res1', '*1*', 'transifex*', '*r*',
-                '*o', 'transifex.tx?', 'transifex.txn',
-            ]
-            results = [
-                ['proj1.res1', ],
-                ['proj1.res1', ],
-                ['transifex.txn', 'transifex.txo', ],
-                ['proj1.res1', 'proj2.res2', 'transifex.txn', 'transifex.txo', ],
-                ['transifex.txo', ],
-                ['transifex.txn', 'transifex.txo', ],
-                ['transifex.txn', ],
-                [],
-            ]
-
-            for i, arg in enumerate(cmd_args):
-                resources = [arg]
-                self.assertEqual(p.get_chosen_resources(resources), results[i])
-
-            # wrong argument
-            resources = ['*trasnifex*', ]
-            self.assertRaises(Exception, p.get_chosen_resources, resources)
-
-
-class TestProjectMinimumPercent(unittest.TestCase):
-    """Test the minimum-perc option."""
-
-    def setUp(self):
-        super(TestProjectMinimumPercent, self).setUp()
-        self.p = Project(init=False)
-        self.p.minimum_perc = None
-        self.p.resource = "resource"
-
-    def test_cmd_option(self):
-        """Test command-line option."""
-        self.p.minimum_perc = 20
-        results = itertools.cycle([80, 90 ])
-        def side_effect(*args):
-            return results.next()
-
-        with patch.object(self.p, "get_resource_option") as mock:
-            mock.side_effect = side_effect
-            self.assertFalse(self.p._satisfies_min_translated({'completed': '12%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '20%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '30%'}))
-
-    def test_global_only(self):
-        """Test only global option."""
-        results = itertools.cycle([80, None ])
-        def side_effect(*args):
-            return results.next()
-
-        with patch.object(self.p, "get_resource_option") as mock:
-            mock.side_effect = side_effect
-            self.assertFalse(self.p._satisfies_min_translated({'completed': '70%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '80%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '90%'}))
-
-    def test_local_lower_than_global(self):
-        """Test the case where the local option is lower than the global."""
-        results = itertools.cycle([80, 70 ])
-        def side_effect(*args):
-            return results.next()
-
-        with patch.object(self.p, "get_resource_option") as mock:
-            mock.side_effect = side_effect
-            self.assertFalse(self.p._satisfies_min_translated({'completed': '60%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '70%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '80%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '90%'}))
-
-    def test_local_higher_than_global(self):
-        """Test the case where the local option is lower than the global."""
-        results = itertools.cycle([60, 70 ])
-        def side_effect(*args):
-            return results.next()
-
-        with patch.object(self.p, "get_resource_option") as mock:
-            mock.side_effect = side_effect
-            self.assertFalse(self.p._satisfies_min_translated({'completed': '60%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '70%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '80%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '90%'}))
-
-    def test_local_only(self):
-        """Test the case where the local option is lower than the global."""
-        results = itertools.cycle([None, 70 ])
-        def side_effect(*args):
-            return results.next()
-
-        with patch.object(self.p, "get_resource_option") as mock:
-            mock.side_effect = side_effect
-            self.assertFalse(self.p._satisfies_min_translated({'completed': '60%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '70%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '80%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '90%'}))
-
-    def test_no_option(self):
-        """"Test the case there is nothing defined."""
-        results = itertools.cycle([None, None ])
-        def side_effect(*args):
-            return results.next()
-
-        with patch.object(self.p, "get_resource_option") as mock:
-            mock.side_effect = side_effect
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '0%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '10%'}))
-            self.assertTrue(self.p._satisfies_min_translated({'completed': '90%'}))
-
-
-class TestProjectFilters(unittest.TestCase):
-    """Test filters used to decide whether to push/pull a translation or not."""
-
-    def setUp(self):
-        super(TestProjectFilters, self).setUp()
-        self.p = Project(init=False)
-        self.p.minimum_perc = None
-        self.p.resource = "resource"
-        self.stats = {
-            'en': {
-                'completed': '100%', 'last_update': '2011-11-01 15:00:00',
-            }, 'el': {
-                'completed': '60%', 'last_update': '2011-11-01 15:00:00',
-            }, 'pt': {
-                'completed': '70%', 'last_update': '2011-11-01 15:00:00',
-            },
-        }
-        self.langs = self.stats.keys()
-
-    def test_add_translation(self):
-        """Test filters for adding translations.
-
-        We do not test here for minimum percentages.
-        """
-        with patch.object(self.p, "get_resource_option") as mock:
-            mock.return_value = None
-            should_add = self.p._should_add_translation
-            for force in [True, False]:
-                for lang in self.langs:
-                    self.assertTrue(should_add(lang, self.stats, force))
-
-            # unknown language
-            self.assertFalse(should_add('es', self.stats))
-
-    def test_update_translation(self):
-        """Test filters for updating a translation.
-
-        We do not test here for minimum percentages.
-        """
-        with patch.object(self.p, "get_resource_option") as mock:
-            mock.return_value = None
-
-            should_update = self.p._should_update_translation
-            force = True
-            for lang in self.langs:
-                self.assertTrue(should_update(lang, self.stats, 'foo', force))
-
-            force = False       # reminder
-            local_file = 'foo'
-
-            # unknown language
-            self.assertFalse(should_update('es', self.stats, local_file))
-
-            # no local file
-            with patch.object(self.p, "_get_time_of_local_file") as time_mock:
-                time_mock.return_value = None
-                with patch.object(self.p, "get_full_path") as path_mock:
-                    path_mock.return_value = "foo"
-                    for lang in self.langs:
-                        self.assertTrue(
-                            should_update(lang, self.stats, local_file)
-                        )
-
-            # older local files
-            local_times = [self.p._generate_timestamp('2011-11-01 14:00:59')]
-            results = itertools.cycle(local_times)
-            def side_effect(*args):
-                return results.next()
-
-            with patch.object(self.p, "_get_time_of_local_file") as time_mock:
-                time_mock.side_effect = side_effect
-                with patch.object(self.p, "get_full_path") as path_mock:
-                    path_mock.return_value = "foo"
-                    for lang in self.langs:
-                        self.assertTrue(
-                            should_update(lang, self.stats, local_file)
-                        )
-
-            # newer local files
-            local_times = [self.p._generate_timestamp('2011-11-01 15:01:59')]
-            results = itertools.cycle(local_times)
-            def side_effect(*args):
-                return results.next()
-
-            with patch.object(self.p, "_get_time_of_local_file") as time_mock:
-                time_mock.side_effect = side_effect
-                with patch.object(self.p, "get_full_path") as path_mock:
-                    path_mock.return_value = "foo"
-                    for lang in self.langs:
-                        self.assertFalse(
-                            should_update(lang, self.stats, local_file)
-                        )
-
-    def test_push_translation(self):
-        """Test filters for pushing a translation file."""
-        with patch.object(self.p, "get_resource_option") as mock:
-            mock.return_value = None
-
-            local_file = 'foo'
-            should_push = self.p._should_push_translation
-            force = True
-            for lang in self.langs:
-                self.assertTrue(should_push(lang, self.stats, local_file, force))
-
-            force = False       # reminder
-
-            # unknown language
-            self.assertTrue(should_push('es', self.stats, local_file))
-
-            # older local files
-            local_times = [self.p._generate_timestamp('2011-11-01 14:00:59')]
-            results = itertools.cycle(local_times)
-            def side_effect(*args):
-                return results.next()
-
-            with patch.object(self.p, "_get_time_of_local_file") as time_mock:
-                time_mock.side_effect = side_effect
-                with patch.object(self.p, "get_full_path") as path_mock:
-                    path_mock.return_value = "foo"
-                    for lang in self.langs:
-                        self.assertFalse(
-                            should_push(lang, self.stats, local_file)
-                        )
-
-            # newer local files
-            local_times = [self.p._generate_timestamp('2011-11-01 15:01:59')]
-            results = itertools.cycle(local_times)
-            def side_effect(*args):
-                return results.next()
-
-            with patch.object(self.p, "_get_time_of_local_file") as time_mock:
-                time_mock.side_effect = side_effect
-                with patch.object(self.p, "get_full_path") as path_mock:
-                    path_mock.return_value = "foo"
-                    for lang in self.langs:
-                        self.assertTrue(
-                            should_push(lang, self.stats, local_file)
-                        )
-
-
-class TestProjectPull(unittest.TestCase):
-    """Test bits & pieces of the pull method."""
-
-    def setUp(self):
-        super(TestProjectPull, self).setUp()
-        self.p = Project(init=False)
-        self.p.minimum_perc = None
-        self.p.resource = "resource"
-        self.p.host = 'foo'
-        self.p.project_slug = 'foo'
-        self.p.resource_slug = 'foo'
-        self.stats = {
-            'en': {
-                'completed': '100%', 'last_update': '2011-11-01 15:00:00',
-            }, 'el': {
-                'completed': '60%', 'last_update': '2011-11-01 15:00:00',
-            }, 'pt': {
-                'completed': '70%', 'last_update': '2011-11-01 15:00:00',
-            },
-        }
-        self.langs = self.stats.keys()
-        self.files = dict(zip(self.langs, itertools.repeat(None)))
-        self.details = {'available_languages': []}
-        for lang in self.langs:
-            self.details['available_languages'].append({'code': lang})
-        self.slang = 'en'
-        self.lang_map = Flipdict()
-
-    def test_new_translations(self):
-        """Test finding new transaltions to add."""
-        with patch.object(self.p, 'do_url_request') as resource_mock:
-            resource_mock.return_value = json.dumps(self.details)
-            files_keys = self.langs
-            new_trans = self.p._new_translations_to_add
-            for force in [True, False]:
-                res = new_trans(
-                    self.files, self.slang, self.lang_map, self.stats, force
-                )
-                self.assertEquals(res, set([]))
-
-            with patch.object(self.p, '_should_add_translation') as filter_mock:
-                filter_mock.return_value = True
-                for force in [True, False]:
-                    res = new_trans(
-                        {'el': None}, self.slang, self.lang_map, self.stats, force
-                    )
-                    self.assertEquals(res, set(['pt']))
-                for force in [True, False]:
-                    res = new_trans(
-                        {}, self.slang, self.lang_map, self.stats, force
-                    )
-                    self.assertEquals(res, set(['el', 'pt']))
-
-                files = {}
-                files['pt_PT'] = None
-                lang_map = {'pt': 'pt_PT'}
-                for force in [True, False]:
-                    res = new_trans(
-                        files, self.slang, lang_map, self.stats, force
-                    )
-                    self.assertEquals(res, set(['el']))
-
-    def test_languages_to_pull_empty_initial_list(self):
-        """Test determining the languages to pull, when the initial
-        list is empty.
-        """
-        languages = []
-        force = False
-
-        res = self.p._languages_to_pull(
-            languages, self.files, self.lang_map, self.stats, force
-        )
-        existing = res[0]
-        new = res[1]
-        self.assertEquals(existing, set(['el', 'en', 'pt']))
-        self.assertFalse(new)
-
-        del self.files['el']
-        self.files['el-gr'] = None
-        self.lang_map['el'] = 'el-gr'
-        res = self.p._languages_to_pull(
-            languages, self.files, self.lang_map, self.stats, force
-        )
-        existing = res[0]
-        new = res[1]
-        self.assertEquals(existing, set(['el', 'en', 'pt']))
-        self.assertFalse(new)
-
-    def test_languages_to_pull_with_initial_list(self):
-        """Test determining the languages to pull, then there is a
-        language selection from the user.
-        """
-        languages = ['el', 'en']
-        self.lang_map['el'] = 'el-gr'
-        del self.files['el']
-        self.files['el-gr'] = None
-        force = False
-
-        with patch.object(self.p, '_should_add_translation') as mock:
-            mock.return_value = True
-            res = self.p._languages_to_pull(
-                languages, self.files, self.lang_map, self.stats, force
-            )
-            existing = res[0]
-            new = res[1]
-            self.assertEquals(existing, set(['en', 'el-gr', ]))
-            self.assertFalse(new)
-
-            mock.return_value = False
-            res = self.p._languages_to_pull(
-                languages, self.files, self.lang_map, self.stats, force
-            )
-            existing = res[0]
-            new = res[1]
-            self.assertEquals(existing, set(['en', 'el-gr', ]))
-            self.assertFalse(new)
-
-            del self.files['el-gr']
-            mock.return_value = True
-            res = self.p._languages_to_pull(
-                languages, self.files, self.lang_map, self.stats, force
-            )
-            existing = res[0]
-            new = res[1]
-            self.assertEquals(existing, set(['en', ]))
-            self.assertEquals(new, set(['el', ]))
-
-            mock.return_value = False
-            res = self.p._languages_to_pull(
-                languages, self.files, self.lang_map, self.stats, force
-            )
-            existing = res[0]
-            new = res[1]
-            self.assertEquals(existing, set(['en', ]))
-            self.assertEquals(new, set([]))
-
-    def test_in_combination_with_force_option(self):
-        """Test the minumum-perc option along with -f."""
-        with patch.object(self.p, 'get_resource_option') as mock:
-            mock.return_value = 70
-
-            res = self.p._should_download('de', self.stats, None, False)
-            self.assertEquals(res, False)
-            res = self.p._should_download('el', self.stats, None, False)
-            self.assertEquals(res, False)
-            res = self.p._should_download('el', self.stats, None, True)
-            self.assertEquals(res, False)
-            res = self.p._should_download('en', self.stats, None, False)
-            self.assertEquals(res, True)
-            res = self.p._should_download('en', self.stats, None, True)
-            self.assertEquals(res, True)
-
-            with patch.object(self.p, '_remote_is_newer') as local_file_mock:
-                local_file_mock = False
-                res = self.p._should_download('pt', self.stats, None, False)
-                self.assertEquals(res, True)
-                res = self.p._should_download('pt', self.stats, None, True)
-                self.assertEquals(res, True)
-
-
-class TestFormats(unittest.TestCase):
-    """Tests for the supported formats."""
-
-    def setUp(self):
-        self.p = Project(init=False)
-
-    def test_extensions(self):
-        """Test returning the correct extension for a format."""
-        sample_formats = {
-            'PO': {'file-extensions': '.po, .pot'},
-            'QT': {'file-extensions': '.ts'},
-        }
-        extensions = ['.po', '.ts', '', ]
-        with patch.object(self.p, "do_url_request") as mock:
-            mock.return_value = json.dumps(sample_formats)
-            for (type_, ext) in zip(['PO', 'QT', 'NONE', ], extensions):
-                extension = self.p._extension_for(type_)
-                self.assertEquals(extension, ext)
-
-
-class TestOptions(unittest.TestCase):
-    """Test the methods related to parsing the configuration file."""
-
-    def setUp(self):
-        self.p = Project(init=False)
-
-    def test_get_option(self):
-        """Test _get_option method."""
-        with contextlib.nested(
-            patch.object(self.p, 'get_resource_option'),
-            patch.object(self.p, 'config', create=True)
-        ) as (rmock, cmock):
-            rmock.return_value = 'resource'
-            cmock.has_option.return_value = 'main'
-            cmock.get.return_value = 'main'
-            self.assertEqual(self.p._get_option(None, None), 'resource')
-            rmock.return_value = None
-            cmock.has_option.return_value = 'main'
-            cmock.get.return_value = 'main'
-            self.assertEqual(self.p._get_option(None, None), 'main')
-            cmock.has_option.return_value = None
-            self.assertIs(self.p._get_option(None, None), None)
-
-
-class TestConfigurationOptions(unittest.TestCase):
-    """Test the various configuration options."""
-
-    def test_i18n_type(self):
-        p = Project(init=False)
-        type_string = 'type'
-        i18n_type = 'PO'
-        with patch.object(p, 'config', create=True) as config_mock:
-            p.set_i18n_type([], i18n_type)
-            calls = config_mock.method_calls
-            self.assertEquals('set', calls[0][0])
-            self.assertEquals('main', calls[0][1][0])
-            p.set_i18n_type(['transifex.txo'], 'PO')
-            calls = config_mock.method_calls
-            self.assertEquals('set', calls[0][0])
-            p.set_i18n_type(['transifex.txo', 'transifex.txn'], 'PO')
-            calls = config_mock.method_calls
-            self.assertEquals('set', calls[0][0])
-            self.assertEquals('set', calls[1][0])
-
-
-class TestStats(unittest.TestCase):
-    """Test the access to the stats objects."""
-
-    def setUp(self):
-        self.stats = Mock()
-        self.stats.__getitem__ = Mock()
-        self.stats.__getitem__.return_value = '12%'
-
-    def test_field_used_per_mode(self):
-        """Test the fields used for each mode."""
-        Project._extract_completed(self.stats, 'translate')
-        self.stats.__getitem__.assert_called_with('completed')
-        Project._extract_completed(self.stats, 'reviewed')
-        self.stats.__getitem__.assert_called_with('reviewed_percentage')
-

+ 0 - 109
third_party/transifex-client/tx

@@ -1,109 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from optparse import OptionParser, OptionValueError
-import os
-import sys
-
-from txclib import utils
-from txclib import get_version
-from txclib.log import set_log_level, logger
-
-reload(sys) # WTF? Otherwise setdefaultencoding doesn't work
-
-# This block ensures that ^C interrupts are handled quietly.
-try:
-    import signal
-
-    def exithandler(signum,frame):
-        signal.signal(signal.SIGINT, signal.SIG_IGN)
-        signal.signal(signal.SIGTERM, signal.SIG_IGN)
-        sys.exit(1)
-
-    signal.signal(signal.SIGINT, exithandler)
-    signal.signal(signal.SIGTERM, exithandler)
-    if hasattr(signal, 'SIGPIPE'):
-        signal.signal(signal.SIGPIPE, signal.SIG_DFL)
-
-except KeyboardInterrupt:
-    sys.exit(1)
-
-# When we open file with f = codecs.open we specifi FROM what encoding to read
-# This sets the encoding for the strings which are created with f.read()
-sys.setdefaultencoding('utf-8')
-
-
-def main(argv):
-    """
-    Here we parse the flags (short, long) and we instantiate the classes.
-    """
-    usage = "usage: %prog [options] command [cmd_options]"
-    description = "This is the Transifex command line client which"\
-		" allows you to manage your translations locally and sync"\
- 	 	" them with the master Transifex server.\nIf you'd like to"\
-		" check the available commands issue `%prog help` or if you"\
-		" just want help with a specific command issue `%prog help"\
-		" command`"
-
-    parser = OptionParser(
-        usage=usage, version=get_version(), description=description
-    )
-    parser.disable_interspersed_args()
-    parser.add_option(
-        "-d", "--debug", action="store_true", dest="debug",
-        default=False, help=("enable debug messages")
-    )
-    parser.add_option(
-        "-q", "--quiet", action="store_true", dest="quiet",
-        default=False, help="don't print status messages to stdout"
-    )
-    parser.add_option(
-        "-r", "--root", action="store", dest="root_dir", type="string",
-        default=None, help="change root directory (default is cwd)"
-    )
-    parser.add_option(
-        "--traceback", action="store_true", dest="trace", default=False,
-        help="print full traceback on exceptions"
-    )
-    parser.add_option(
-        "--disable-colors", action="store_true", dest="color_disable",
-        default=(os.name == 'nt' or not sys.stdout.isatty()),
-        help="disable colors in the output of commands"
-    )
-    (options, args) = parser.parse_args()
-
-    if len(args) < 1:
-        parser.error("No command was given")
-
-    utils.DISABLE_COLORS = options.color_disable
-
-    # set log level
-    if options.quiet:
-        set_log_level('WARNING')
-    elif options.debug:
-        set_log_level('DEBUG')
-
-    # find .tx
-    path_to_tx = options.root_dir or utils.find_dot_tx()
-
-
-    cmd = args[0]
-    try:
-        utils.exec_command(cmd, args[1:], path_to_tx)
-    except utils.UnknownCommandError:
-        logger.error("tx: Command %s not found" % cmd)
-    except SystemExit:
-        sys.exit()
-    except:
-        import traceback
-        if options.trace:
-            traceback.print_exc()
-        else:
-            formatted_lines = traceback.format_exc().splitlines()
-            logger.error(formatted_lines[-1])
-        sys.exit(1)
-
-# Run baby :) ... run
-if __name__ == "__main__":
-    # sys.argv[0] is the name of the script that we’re running.
-    main(sys.argv[1:])

+ 0 - 19
third_party/transifex-client/txclib/__init__.py

@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-Copyright (C) 2010 by Indifex (www.indifex.com), see AUTHORS.
-License: BSD, see LICENSE for details.
-
-For further information visit http://code.indifex.com/transifex-client
-"""
-
-
-VERSION = (0, 9, 0, 'devel')
-
-def get_version():
-    version = '%s.%s' % (VERSION[0], VERSION[1])
-    if VERSION[2]:
-        version = '%s.%s' % (version, VERSION[2])
-    if VERSION[3] != 'final':
-        version = '%s %s' % (version, VERSION[3])
-    return version

+ 0 - 576
third_party/transifex-client/txclib/commands.py

@@ -1,576 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-In this file we have all the top level commands for the transifex client.
-Since we're using a way to automatically list them and execute them, when
-adding code to this file you must take care of the following:
- * Added functions must begin with 'cmd_' followed by the actual name of the
-   command being used in the command line (eg cmd_init)
- * The description for each function that we display to the user is read from
-   the func_doc attribute which reads the doc string. So, when adding
-   docstring to a new function make sure you add an oneliner which is
-   descriptive and is meant to be seen by the user.
- * When including libraries, it's best if you include modules instead of
-   functions because that way our function resolution will work faster and the
-   chances of overlapping are minimal
- * All functions should use the OptionParser and should have a usage and
-   descripition field.
-"""
-import os
-import re, shutil
-import sys
-from optparse import OptionParser, OptionGroup
-import ConfigParser
-
-
-from txclib import utils, project
-from txclib.utils import parse_json, compile_json, relpath
-from txclib.config import OrderedRawConfigParser
-from txclib.exceptions import UnInitializedError
-from txclib.parsers import delete_parser, help_parser, parse_csv_option, \
-        status_parser, pull_parser, set_parser, push_parser, init_parser
-from txclib.log import logger
-
-
-def cmd_init(argv, path_to_tx):
-    "Initialize a new transifex project."
-    parser = init_parser()
-    (options, args) = parser.parse_args(argv)
-    if len(args) > 1:
-        parser.error("Too many arguments were provided. Aborting...")
-    if args:
-        path_to_tx = args[0]
-    else:
-        path_to_tx = os.getcwd()
-
-    if os.path.isdir(os.path.join(path_to_tx,".tx")):
-        logger.info("tx: There is already a tx folder!")
-        reinit = raw_input("Do you want to delete it and reinit the project? [y/N]: ")
-        while (reinit != 'y' and reinit != 'Y' and reinit != 'N' and reinit != 'n' and reinit != ''):
-            reinit = raw_input("Do you want to delete it and reinit the project? [y/N]: ")
-        if not reinit or reinit in ['N', 'n', 'NO', 'no', 'No']:
-            return
-        # Clean the old settings
-        # FIXME: take a backup
-        else:
-            rm_dir = os.path.join(path_to_tx, ".tx")
-            shutil.rmtree(rm_dir)
-
-    logger.info("Creating .tx folder...")
-    os.mkdir(os.path.join(path_to_tx,".tx"))
-
-    # Handle the credentials through transifexrc
-    home = os.path.expanduser("~")
-    txrc = os.path.join(home, ".transifexrc")
-    config = OrderedRawConfigParser()
-
-    default_transifex = "https://www.transifex.com"
-    transifex_host = options.host or raw_input("Transifex instance [%s]: " % default_transifex)
-
-    if not transifex_host:
-        transifex_host = default_transifex
-    if not transifex_host.startswith(('http://', 'https://')):
-        transifex_host = 'https://' + transifex_host
-
-    config_file = os.path.join(path_to_tx, ".tx", "config")
-    if not os.path.exists(config_file):
-        # The path to the config file (.tx/config)
-        logger.info("Creating skeleton...")
-        config = OrderedRawConfigParser()
-        config.add_section('main')
-        config.set('main', 'host', transifex_host)
-        # Touch the file if it doesn't exist
-        logger.info("Creating config file...")
-        fh = open(config_file, 'w')
-        config.write(fh)
-        fh.close()
-
-    prj = project.Project(path_to_tx)
-    prj.getset_host_credentials(transifex_host, user=options.user,
-        password=options.password)
-    prj.save()
-    logger.info("Done.")
-
-
-def cmd_set(argv, path_to_tx):
-    "Add local or remote files under transifex"
-    parser = set_parser()
-    (options, args) = parser.parse_args(argv)
-
-    # Implement options/args checks
-    # TODO !!!!!!!
-    if options.local:
-        try:
-            expression = args[0]
-        except IndexError:
-            parser.error("Please specify an expression.")
-        if not options.resource:
-            parser.error("Please specify a resource")
-        if not options.source_language:
-            parser.error("Please specify a source language.")
-        if not '<lang>' in expression:
-            parser.error("The expression you have provided is not valid.")
-        if not utils.valid_slug(options.resource):
-            parser.error("Invalid resource slug. The format is <project_slug>"\
-                ".<resource_slug> and the valid characters include [_-\w].")
-        _auto_local(path_to_tx, options.resource,
-            source_language=options.source_language,
-            expression = expression, source_file=options.source_file,
-            execute=options.execute, regex=False)
-        if options.execute:
-            _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx)
-            _set_mode(options.resource, options.mode, path_to_tx)
-            _set_type(options.resource, options.i18n_type, path_to_tx)
-        return
-
-    if options.remote:
-        try:
-            url = args[0]
-        except IndexError:
-            parser.error("Please specify an remote url")
-        _auto_remote(path_to_tx, url)
-        _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx)
-        _set_mode(options.resource, options.mode, path_to_tx)
-        return
-
-    if options.is_source:
-        resource = options.resource
-        if not resource:
-            parser.error("You must specify a resource name with the"
-                " -r|--resource flag.")
-
-        lang = options.language
-        if not lang:
-            parser.error("Please specify a source language.")
-
-        if len(args) != 1:
-            parser.error("Please specify a file.")
-
-        if not utils.valid_slug(resource):
-            parser.error("Invalid resource slug. The format is <project_slug>"\
-                ".<resource_slug> and the valid characters include [_-\w].")
-
-        file = args[0]
-        # Calculate relative path
-        path_to_file = relpath(file, path_to_tx)
-        _set_source_file(path_to_tx, resource, options.language, path_to_file)
-    elif options.resource or options.language:
-        resource = options.resource
-        lang = options.language
-
-        if len(args) != 1:
-            parser.error("Please specify a file")
-
-        # Calculate relative path
-        path_to_file = relpath(args[0], path_to_tx)
-
-        try:
-            _go_to_dir(path_to_tx)
-        except UnInitializedError, e:
-            utils.logger.error(e)
-            return
-
-        if not utils.valid_slug(resource):
-            parser.error("Invalid resource slug. The format is <project_slug>"\
-                ".<resource_slug> and the valid characters include [_-\w].")
-        _set_translation(path_to_tx, resource, lang, path_to_file)
-
-    _set_mode(options.resource, options.mode, path_to_tx)
-    _set_type(options.resource, options.i18n_type, path_to_tx)
-    _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx)
-
-    logger.info("Done.")
-    return
-
-
-def _auto_local(path_to_tx, resource, source_language, expression, execute=False,
-                source_file=None, regex=False):
-    """Auto configure local project."""
-    # The path everything will be relative to
-    curpath = os.path.abspath(os.curdir)
-
-    # Force expr to be a valid regex expr (escaped) but keep <lang> intact
-    expr_re = utils.regex_from_filefilter(expression, curpath)
-    expr_rec = re.compile(expr_re)
-
-    if not execute:
-        logger.info("Only printing the commands which will be run if the "
-                  "--execute switch is specified.")
-
-    # First, let's construct a dictionary of all matching files.
-    # Note: Only the last matching file of a language will be stored.
-    translation_files = {}
-    for root, dirs, files in os.walk(curpath):
-        for f in files:
-            f_path = os.path.abspath(os.path.join(root, f))
-            match = expr_rec.match(f_path)
-            if match:
-                lang = match.group(1)
-                f_path = os.path.abspath(f_path)
-                if lang == source_language and not source_file:
-                    source_file = f_path
-                else:
-                    translation_files[lang] = f_path
-
-    if not source_file:
-        raise Exception("Could not find a source language file. Please run"
-            " set --source manually and then re-run this command or provide"
-            " the source file with the -s flag.")
-    if execute:
-        logger.info("Updating source for resource %s ( %s -> %s )." % (resource,
-            source_language, relpath(source_file, path_to_tx)))
-        _set_source_file(path_to_tx, resource, source_language,
-            relpath(source_file, path_to_tx))
-    else:
-        logger.info('\ntx set --source -r %(res)s -l %(lang)s %(file)s\n' % {
-            'res': resource,
-            'lang': source_language,
-            'file': relpath(source_file, curpath)})
-
-    prj = project.Project(path_to_tx)
-    root_dir = os.path.abspath(path_to_tx)
-
-    if execute:
-        try:
-            prj.config.get("%s" % resource, "source_file")
-        except ConfigParser.NoSectionError:
-            raise Exception("No resource with slug \"%s\" was found.\nRun 'tx set --auto"
-                "-local -r %s \"expression\"' to do the initial configuration." % resource)
-
-    # Now let's handle the translation files.
-    if execute:
-        logger.info("Updating file expression for resource %s ( %s )." % (resource,
-            expression))
-        # Eval file_filter relative to root dir
-        file_filter = relpath(os.path.join(curpath, expression),
-            path_to_tx)
-        prj.config.set("%s" % resource, "file_filter", file_filter)
-    else:
-        for (lang, f_path) in sorted(translation_files.items()):
-            logger.info('tx set -r %(res)s -l %(lang)s %(file)s' % {
-                'res': resource,
-                'lang': lang,
-                'file': relpath(f_path, curpath)})
-
-    if execute:
-        prj.save()
-
-
-def _auto_remote(path_to_tx, url):
-    """
-    Initialize a remote release/project/resource to the current directory.
-    """
-    logger.info("Auto configuring local project from remote URL...")
-
-    type, vars = utils.parse_tx_url(url)
-    prj = project.Project(path_to_tx)
-    username, password = prj.getset_host_credentials(vars['hostname'])
-
-    if type == 'project':
-        logger.info("Getting details for project %s" % vars['project'])
-        proj_info = utils.get_details('project_details',
-            username, password,
-            hostname = vars['hostname'], project = vars['project'])
-        resources = [ '.'.join([vars['project'], r['slug']]) for r in proj_info['resources'] ]
-        logger.info("%s resources found. Configuring..." % len(resources))
-    elif type == 'release':
-        logger.info("Getting details for release %s" % vars['release'])
-        rel_info = utils.get_details('release_details',
-            username, password, hostname = vars['hostname'],
-            project = vars['project'], release = vars['release'])
-        resources = []
-        for r in rel_info['resources']:
-            if r.has_key('project'):
-                resources.append('.'.join([r['project']['slug'], r['slug']]))
-            else:
-                resources.append('.'.join([vars['project'], r['slug']]))
-        logger.info("%s resources found. Configuring..." % len(resources))
-    elif type == 'resource':
-        logger.info("Getting details for resource %s" % vars['resource'])
-        resources = [ '.'.join([vars['project'], vars['resource']]) ]
-    else:
-        raise("Url '%s' is not recognized." % url)
-
-    for resource in resources:
-        logger.info("Configuring resource %s." % resource)
-        proj, res = resource.split('.')
-        res_info = utils.get_details('resource_details',
-             username, password, hostname = vars['hostname'],
-             project = proj, resource=res)
-        try:
-            source_lang = res_info['source_language_code']
-            i18n_type = res_info['i18n_type']
-        except KeyError:
-            raise Exception("Remote server seems to be running an unsupported version"
-                " of Transifex. Either update your server software of fallback"
-                " to a previous version of transifex-client.")
-        prj.set_remote_resource(
-            resource=resource,
-            host = vars['hostname'],
-            source_lang = source_lang,
-            i18n_type = i18n_type)
-
-    prj.save()
-
-
-def cmd_push(argv, path_to_tx):
-    "Push local files to remote server"
-    parser = push_parser()
-    (options, args) = parser.parse_args(argv)
-    force_creation = options.force_creation
-    languages = parse_csv_option(options.languages)
-    resources = parse_csv_option(options.resources)
-    skip = options.skip_errors
-    prj = project.Project(path_to_tx)
-    if not (options.push_source or options.push_translations):
-        parser.error("You need to specify at least one of the -s|--source,"
-            " -t|--translations flags with the push command.")
-
-    prj.push(
-        force=force_creation, resources=resources, languages=languages,
-        skip=skip, source=options.push_source,
-        translations=options.push_translations,
-        no_interactive=options.no_interactive
-    )
-    logger.info("Done.")
-
-
-def cmd_pull(argv, path_to_tx):
-    "Pull files from remote server to local repository"
-    parser = pull_parser()
-    (options, args) = parser.parse_args(argv)
-    if options.fetchall and options.languages:
-        parser.error("You can't user a language filter along with the"\
-            " -a|--all option")
-    languages = parse_csv_option(options.languages)
-    resources = parse_csv_option(options.resources)
-    skip = options.skip_errors
-    minimum_perc = options.minimum_perc or None
-
-    try:
-        _go_to_dir(path_to_tx)
-    except UnInitializedError, e:
-        utils.logger.error(e)
-        return
-
-    # instantiate the project.Project
-    prj = project.Project(path_to_tx)
-    prj.pull(
-        languages=languages, resources=resources, overwrite=options.overwrite,
-        fetchall=options.fetchall, fetchsource=options.fetchsource,
-        force=options.force, skip=skip, minimum_perc=minimum_perc,
-        mode=options.mode
-    )
-    logger.info("Done.")
-
-
-def _set_source_file(path_to_tx, resource, lang, path_to_file):
-    """Reusable method to set source file."""
-    proj, res = resource.split('.')
-    if not proj or not res:
-        raise Exception("\"%s.%s\" is not a valid resource identifier. It should"
-            " be in the following format project_slug.resource_slug." %
-            (proj, res))
-    if not lang:
-        raise Exception("You haven't specified a source language.")
-
-    try:
-        _go_to_dir(path_to_tx)
-    except UnInitializedError, e:
-        utils.logger.error(e)
-        return
-
-    if not os.path.exists(path_to_file):
-        raise Exception("tx: File ( %s ) does not exist." %
-            os.path.join(path_to_tx, path_to_file))
-
-    # instantiate the project.Project
-    prj = project.Project(path_to_tx)
-    root_dir = os.path.abspath(path_to_tx)
-
-    if root_dir not in os.path.normpath(os.path.abspath(path_to_file)):
-        raise Exception("File must be under the project root directory.")
-
-    logger.info("Setting source file for resource %s.%s ( %s -> %s )." % (
-        proj, res, lang, path_to_file))
-
-    path_to_file = relpath(path_to_file, root_dir)
-
-    prj = project.Project(path_to_tx)
-
-    # FIXME: Check also if the path to source file already exists.
-    try:
-        try:
-            prj.config.get("%s.%s" % (proj, res), "source_file")
-        except ConfigParser.NoSectionError:
-            prj.config.add_section("%s.%s" % (proj, res))
-        except ConfigParser.NoOptionError:
-            pass
-    finally:
-        prj.config.set("%s.%s" % (proj, res), "source_file",
-           path_to_file)
-        prj.config.set("%s.%s" % (proj, res), "source_lang",
-            lang)
-
-    prj.save()
-
-
-def _set_translation(path_to_tx, resource, lang, path_to_file):
-    """Reusable method to set translation file."""
-
-    proj, res = resource.split('.')
-    if not project or not resource:
-        raise Exception("\"%s\" is not a valid resource identifier. It should"
-            " be in the following format project_slug.resource_slug." %
-            resource)
-
-    try:
-        _go_to_dir(path_to_tx)
-    except UnInitializedError, e:
-        utils.logger.error(e)
-        return
-
-    # Warn the user if the file doesn't exist
-    if not os.path.exists(path_to_file):
-        logger.info("Warning: File '%s' doesn't exist." % path_to_file)
-
-    # instantiate the project.Project
-    prj = project.Project(path_to_tx)
-    root_dir = os.path.abspath(path_to_tx)
-
-    if root_dir not in os.path.normpath(os.path.abspath(path_to_file)):
-        raise Exception("File must be under the project root directory.")
-
-    if lang ==  prj.config.get("%s.%s" % (proj, res), "source_lang"):
-        raise Exception("tx: You cannot set translation file for the source language."
-            " Source languages contain the strings which will be translated!")
-
-    logger.info("Updating translations for resource %s ( %s -> %s )." % (resource,
-        lang, path_to_file))
-    path_to_file = relpath(path_to_file, root_dir)
-    prj.config.set("%s.%s" % (proj, res), "trans.%s" % lang,
-        path_to_file)
-
-    prj.save()
-
-
-def cmd_status(argv, path_to_tx):
-    "Print status of current project"
-    parser = status_parser()
-    (options, args) = parser.parse_args(argv)
-    resources = parse_csv_option(options.resources)
-    prj = project.Project(path_to_tx)
-    resources = prj.get_chosen_resources(resources)
-    resources_num = len(resources)
-    for idx, res in enumerate(resources):
-        p, r = res.split('.')
-        logger.info("%s -> %s (%s of %s)" % (p, r, idx + 1, resources_num))
-        logger.info("Translation Files:")
-        slang = prj.get_resource_option(res, 'source_lang')
-        sfile = prj.get_resource_option(res, 'source_file') or "N/A"
-        lang_map = prj.get_resource_lang_mapping(res)
-        logger.info(" - %s: %s (%s)" % (utils.color_text(slang, "RED"),
-            sfile, utils.color_text("source", "YELLOW")))
-        files = prj.get_resource_files(res)
-        fkeys = files.keys()
-        fkeys.sort()
-        for lang in fkeys:
-            local_lang = lang
-            if lang in lang_map.values():
-                local_lang = lang_map.flip[lang]
-            logger.info(" - %s: %s" % (utils.color_text(local_lang, "RED"),
-                files[lang]))
-        logger.info("")
-
-
-def cmd_help(argv, path_to_tx):
-    """List all available commands"""
-    parser = help_parser()
-    (options, args) = parser.parse_args(argv)
-    if len(args) > 1:
-        parser.error("Multiple arguments received. Exiting...")
-
-    # Get all commands
-    fns = utils.discover_commands()
-
-    # Print help for specific command
-    if len(args) == 1:
-        try:
-            fns[argv[0]](['--help'], path_to_tx)
-        except KeyError:
-            utils.logger.error("Command %s not found" % argv[0])
-    # or print summary of all commands
-
-    # the code below will only be executed if the KeyError exception is thrown
-    # becuase in all other cases the function called with --help will exit
-    # instead of return here
-    keys = fns.keys()
-    keys.sort()
-
-    logger.info("Transifex command line client.\n")
-    logger.info("Available commands are:")
-    for key in keys:
-        logger.info("  %-15s\t%s" % (key, fns[key].func_doc))
-    logger.info("\nFor more information run %s command --help" % sys.argv[0])
-
-
-def cmd_delete(argv, path_to_tx):
-    "Delete an accessible resource or translation in a remote server."
-    parser = delete_parser()
-    (options, args) = parser.parse_args(argv)
-    languages = parse_csv_option(options.languages)
-    resources = parse_csv_option(options.resources)
-    skip = options.skip_errors
-    force = options.force_delete
-    prj = project.Project(path_to_tx)
-    prj.delete(resources, languages, skip, force)
-    logger.info("Done.")
-
-
-def _go_to_dir(path):
-    """Change the current working directory to the directory specified as
-    argument.
-
-    Args:
-        path: The path to chdor to.
-    Raises:
-        UnInitializedError, in case the directory has not been initialized.
-    """
-    if path is None:
-        raise UnInitializedError(
-            "Directory has not been initialzied. "
-            "Did you forget to run 'tx init' first?"
-        )
-    os.chdir(path)
-
-
-def _set_minimum_perc(resource, value, path_to_tx):
-    """Set the minimum percentage in the .tx/config file."""
-    args = (resource, 'minimum_perc', value, path_to_tx, 'set_min_perc')
-    _set_project_option(*args)
-
-
-def _set_mode(resource, value, path_to_tx):
-    """Set the mode in the .tx/config file."""
-    args = (resource, 'mode', value, path_to_tx, 'set_default_mode')
-    _set_project_option(*args)
-
-
-def _set_type(resource, value, path_to_tx):
-    """Set the i18n type in the .tx/config file."""
-    args = (resource, 'type', value, path_to_tx, 'set_i18n_type')
-    _set_project_option(*args)
-
-
-def _set_project_option(resource, name, value, path_to_tx, func_name):
-    """Save the option to the project config file."""
-    if value is None:
-        return
-    if not resource:
-        logger.debug("Setting the %s for all resources." % name)
-        resources = []
-    else:
-        logger.debug("Setting the %s for resource %s." % (name, resource))
-        resources = [resource, ]
-    prj = project.Project(path_to_tx)
-    getattr(prj, func_name)(resources, value)
-    prj.save()

+ 0 - 115
third_party/transifex-client/txclib/config.py

@@ -1,115 +0,0 @@
-import ConfigParser
-
-
-class OrderedRawConfigParser( ConfigParser.RawConfigParser ):
-    """
-    Overload standard Class ConfigParser.RawConfigParser
-    """
-    def write(self, fp):
-        """Write an .ini-format representation of the configuration state."""
-        if self._defaults:
-            fp.write("[%s]\n" % DEFAULTSECT)
-            for key in sorted( self._defaults ):
-                fp.write( "%s = %s\n" % (key, str( self._defaults[ key ]
-                    ).replace('\n', '\n\t')) )
-            fp.write("\n")
-        for section in self._sections:
-            fp.write("[%s]\n" % section)
-            for key in sorted( self._sections[section] ):
-                if key != "__name__":
-                    fp.write("%s = %s\n" %
-                        (key, str( self._sections[section][ key ]
-                        ).replace('\n', '\n\t')))
-            fp.write("\n")
-
-    optionxform = str
-
-
-_NOTFOUND = object()
-
-
-class Flipdict(dict):
-    """An injective (one-to-one) python dict.  Ensures that each key maps
-    to a unique value, and each value maps back to that same key.
-
-    Code mostly taken from here:
-    http://code.activestate.com/recipes/576968-flipdict-python-dict-that-also-maintains-a-one-to-/
-    """
-
-    def __init__(self, *args, **kw):
-        self._flip = dict.__new__(self.__class__)
-        setattr(self._flip, "_flip", self)
-        for key, val in dict(*args, **kw).iteritems():
-            self[key] = val
-
-    @property
-    def flip(self):
-        """The inverse mapping."""
-        return self._flip
-
-    def __repr__(self):
-        return "%s(%r)" % (self.__class__.__name__, dict(self))
-
-    __str__ = __repr__
-
-    def copy(self):
-        return self.__class__(self)
-
-    @classmethod
-    def fromkeys(cls, keys, value=None):
-        return cls(dict.fromkeys(keys, value))
-
-    def __setitem__(self, key, val):
-        k = self._flip.get(val, _NOTFOUND)
-        if not (k is _NOTFOUND or k==key):
-            raise KeyError('(key,val) would erase mapping for value %r' % val)
-
-        v = self.get(key, _NOTFOUND)
-        if v is not _NOTFOUND:
-            dict.__delitem__(self._flip, v)
-
-        dict.__setitem__(self,       key, val)
-        dict.__setitem__(self._flip, val, key)
-
-    def setdefault(self, key, default = None):
-        # Copied from python's UserDict.DictMixin code.
-        try:
-            return self[key]
-        except KeyError:
-            self[key] = default
-            return default
-
-    def update(self, other = None, **kwargs):
-        # Copied from python's UserDict.DictMixin code.
-        # Make progressively weaker assumptions about "other"
-        if other is None:
-            pass
-        elif hasattr(other, 'iteritems'):  # iteritems saves memory and lookups
-            for k, v in other.iteritems():
-                self[k] = v
-        elif hasattr(other, 'keys'):
-            for k in other.keys():
-                self[k] = other[k]
-        else:
-            for k, v in other:
-                self[k] = v
-        if kwargs:
-            self.update(kwargs)
-
-    def __delitem__(self, key):
-        val = dict.pop(self, key)
-        dict.__delitem__(self._flip, val)
-
-    def pop(self, key, *args):
-        val = dict.pop(self, key, *args)
-        dict.__delitem__(self._flip, val)
-        return val
-
-    def popitem(self):
-        key, val = dict.popitem(self)
-        dict.__delitem__(self._flip, val)
-        return key, val
-
-    def clear(self):
-        dict.clear(self)
-        dict.clear(self._flip)

+ 0 - 13
third_party/transifex-client/txclib/exceptions.py

@@ -1,13 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-Exception classes for the tx client.
-"""
-
-
-class UnInitializedError(Exception):
-    """The project directory has not been initialized."""
-
-
-class UnknownCommandError(Exception):
-    """The provided command is not supported."""

+ 0 - 46
third_party/transifex-client/txclib/http_utils.py

@@ -1,46 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-HTTP-related utility functions.
-"""
-
-from __future__ import with_statement
-import gzip
-try:
-    import cStringIO as StringIO
-except ImportError:
-    import StringIO
-
-
-def _gzip_decode(gzip_data):
-    """
-    Unzip gzipped data and return them.
-
-    :param gzip_data: Gzipped data.
-    :returns: The actual data.
-    """
-    try:
-        gzip_data = StringIO.StringIO(gzip_data)
-        gzip_file = gzip.GzipFile(fileobj=gzip_data)
-        data = gzip_file.read()
-        return data
-    finally:
-        gzip_data.close()
-
-
-def http_response(response):
-    """
-    Return the response of a HTTP request.
-
-    If the response has been gzipped, gunzip it first.
-
-    :param response: The raw response of a HTTP request.
-    :returns: A response suitable to be used by clients.
-    """
-    metadata = response.info()
-    data = response.read()
-    response.close()
-    if metadata.get('content-encoding') == 'gzip':
-        return _gzip_decode(data)
-    else:
-        return data

+ 0 - 37
third_party/transifex-client/txclib/log.py

@@ -1,37 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-Add logging capabilities to tx-client.
-"""
-
-import sys
-import logging
-
-_logger = logging.getLogger('txclib')
-_logger.setLevel(logging.INFO)
-
-_formatter = logging.Formatter('%(message)s')
-
-_error_handler = logging.StreamHandler(sys.stderr)
-_error_handler.setLevel(logging.ERROR)
-_error_handler.setFormatter(_formatter)
-_logger.addHandler(_error_handler)
-
-_msg_handler = logging.StreamHandler(sys.stdout)
-_msg_handler.setLevel(logging.DEBUG)
-_msg_handler.setFormatter(_formatter)
-_msg_filter = logging.Filter()
-_msg_filter.filter = lambda r: r.levelno < logging.ERROR
-_msg_handler.addFilter(_msg_filter)
-_logger.addHandler(_msg_handler)
-
-logger = _logger
-
-
-def set_log_level(level):
-    """Set the level for the logger.
-
-    Args:
-        level: A string among DEBUG, INFO, WARNING, ERROR, CRITICAL.
-    """
-    logger.setLevel(getattr(logging, level))

+ 0 - 241
third_party/transifex-client/txclib/parsers.py

@@ -1,241 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from optparse import OptionParser, OptionGroup
-
-
-class EpilogParser(OptionParser):
-    def format_epilog(self, formatter):
-        return self.epilog
-
-
-def delete_parser():
-    """Return the command-line parser for the delete command."""
-    usage = "usage: %prog [tx_options] delete OPTION [OPTIONS]"
-    description = (
-        "This command deletes translations for a resource in the remote server."
-    )
-    epilog = (
-        "\nExamples:\n"
-        " To delete a translation:\n  "
-        "$ tx delete -r project.resource -l <lang_code>\n\n"
-        " To delete a resource:\n  $ tx delete -r project.resource\n"
-    )
-    parser = EpilogParser(usage=usage, description=description, epilog=epilog)
-    parser.add_option(
-        "-r", "--resource", action="store", dest="resources", default=None,
-        help="Specify the resource you want to delete (defaults to all)"
-    )
-    parser.add_option(
-        "-l","--language", action="store", dest="languages",
-        default=None, help="Specify the translation you want to delete"
-    )
-    parser.add_option(
-        "--skip", action="store_true", dest="skip_errors", default=False,
-        help="Don't stop on errors."
-    )
-    parser.add_option(
-        "-f","--force", action="store_true", dest="force_delete",
-        default=False, help="Delete an entity forcefully."
-    )
-    return parser
-
-
-def help_parser():
-    """Return the command-line parser for the help command."""
-    usage="usage: %prog help command"
-    description="Lists all available commands in the transifex command"\
-        " client. If a command is specified, the help page of the specific"\
-        " command is displayed instead."
-
-    parser = OptionParser(usage=usage, description=description)
-    return parser
-
-
-def init_parser():
-    """Return the command-line parser for the init command."""
-    usage="usage: %prog [tx_options] init <path>"
-    description="This command initializes a new project for use with"\
-        " transifex. It is recommended to execute this command in the"\
-        " top level directory of your project so that you can include"\
-        " all files under it in transifex. If no path is provided, the"\
-        " current working dir will be used."
-    parser = OptionParser(usage=usage, description=description)
-    parser.add_option("--host", action="store", dest="host",
-        default=None, help="Specify a default Transifex host.")
-    parser.add_option("--user", action="store", dest="user",
-        default=None, help="Specify username for Transifex server.")
-    parser.add_option("--pass", action="store", dest="password",
-        default=None, help="Specify password for Transifex server.")
-    return parser
-
-
-def pull_parser():
-    """Return the command-line parser for the pull command."""
-    usage="usage: %prog [tx_options] pull [options]"
-    description="This command pulls all outstanding changes from the remote"\
-        " Transifex server to the local repository. By default, only the"\
-        " files that are watched by Transifex will be updated but if you"\
-        " want to fetch the translations for new languages as well, use the"\
-        " -a|--all option. (Note: new translations are saved in the .tx folder"\
-        " and require the user to manually rename them and add then in "\
-        " transifex using the set_translation command)."
-    parser = OptionParser(usage=usage,description=description)
-    parser.add_option("-l","--language", action="store", dest="languages",
-        default=[], help="Specify which translations you want to pull"
-        " (defaults to all)")
-    parser.add_option("-r","--resource", action="store", dest="resources",
-        default=[], help="Specify the resource for which you want to pull"
-        " the translations (defaults to all)")
-    parser.add_option("-a","--all", action="store_true", dest="fetchall",
-        default=False, help="Fetch all translation files from server (even new"
-        " ones)")
-    parser.add_option("-s","--source", action="store_true", dest="fetchsource",
-        default=False, help="Force the fetching of the source file (default:"
-        " False)")
-    parser.add_option("-f","--force", action="store_true", dest="force",
-        default=False, help="Force download of translations files.")
-    parser.add_option("--skip", action="store_true", dest="skip_errors",
-        default=False, help="Don't stop on errors. Useful when pushing many"
-        " files concurrently.")
-    parser.add_option("--disable-overwrite", action="store_false",
-        dest="overwrite", default=True,
-        help="By default transifex will fetch new translations files and"\
-            " replace existing ones. Use this flag if you want to disable"\
-            " this feature")
-    parser.add_option("--minimum-perc", action="store", type="int",
-        dest="minimum_perc", default=0,
-        help="Specify the minimum acceptable percentage of a translation "
-             "in order to download it.")
-    parser.add_option(
-        "--mode", action="store", dest="mode", help=(
-            "Specify the mode of the translation file to pull (e.g. "
-            "'reviewed'). See http://bit.ly/txcmod1 for available values."
-        )
-    )
-    return parser
-
-
-def push_parser():
-    """Return the command-line parser for the push command."""
-    usage="usage: %prog [tx_options] push [options]"
-    description="This command pushes all local files that have been added to"\
-        " Transifex to the remote server. All new translations are merged"\
-        " with existing ones and if a language doesn't exists then it gets"\
-        " created. If you want to push the source file as well (either"\
-        " because this is your first time running the client or because"\
-        " you just have updated with new entries), use the -f|--force option."\
-        " By default, this command will push all files which are watched by"\
-        " Transifex but you can filter this per resource or/and language."
-    parser = OptionParser(usage=usage, description=description)
-    parser.add_option("-l","--language", action="store", dest="languages",
-        default=None, help="Specify which translations you want to push"
-        " (defaults to all)")
-    parser.add_option("-r","--resource", action="store", dest="resources",
-        default=None, help="Specify the resource for which you want to push"
-        " the translations (defaults to all)")
-    parser.add_option("-f","--force", action="store_true", dest="force_creation",
-        default=False, help="Push source files without checking modification"
-        " times.")
-    parser.add_option("--skip", action="store_true", dest="skip_errors",
-        default=False, help="Don't stop on errors. Useful when pushing many"
-        " files concurrently.")
-    parser.add_option("-s", "--source", action="store_true", dest="push_source",
-        default=False, help="Push the source file to the server.")
-
-    parser.add_option("-t", "--translations", action="store_true", dest="push_translations",
-        default=False, help="Push the translation files to the server")
-    parser.add_option("--no-interactive", action="store_true", dest="no_interactive",
-        default=False, help="Don't require user input when forcing a push.")
-    return parser
-
-
-def set_parser():
-    """Return the command-line parser for the set command."""
-    usage="usage: %prog [tx_options] set [options] [args]"
-    description="This command can be used to create a mapping between files"\
-        " and projects either using local files or using files from a remote"\
-        " Transifex server."
-    epilog="\nExamples:\n"\
-        " To set the source file:\n  $ tx set -r project.resource --source -l en <file>\n\n"\
-        " To set a single translation file:\n  $ tx set -r project.resource -l de <file>\n\n"\
-        " To automatically detect and assign the source files and translations:\n"\
-        "  $ tx set --auto-local -r project.resource 'expr' --source-lang en\n\n"\
-        " To set a specific file as a source and auto detect translations:\n"\
-        "  $ tx set --auto-local -r project.resource 'expr' --source-lang en"\
-        " --source-file <file>\n\n"\
-        " To set a remote release/resource/project:\n"\
-        "  $ tx set --auto-remote <transifex-url>\n"
-    parser = EpilogParser(usage=usage, description=description, epilog=epilog)
-    parser.add_option("--auto-local", action="store_true", dest="local",
-        default=False, help="Used when auto configuring local project.")
-    parser.add_option("--auto-remote", action="store_true", dest="remote",
-        default=False, help="Used when adding remote files from Transifex"
-        " server.")
-    parser.add_option("-r","--resource", action="store", dest="resource",
-        default=None, help="Specify the slug of the resource that you're"
-            " setting up (This must be in the following format:"
-            " `project_slug.resource_slug`).")
-    parser.add_option(
-        "--source", action="store_true", dest="is_source", default=False,
-        help=(
-            "Specify that the given file is a source file "
-            "[doesn't work with the --auto-* commands]."
-        )
-    )
-    parser.add_option("-l","--language", action="store", dest="language",
-        default=None, help="Specify which translations you want to pull"
-        " [doesn't work with the --auto-* commands].")
-    parser.add_option("-t", "--type", action="store", dest="i18n_type",
-        help=(
-            "Specify the i18n type of the resource(s). This is only needed, if "
-            "the resource(s) does not exist yet in Transifex. For a list of "
-            "available i18n types, see "
-            "http://help.transifex.com/features/formats.html"
-        )
-    )
-    parser.add_option("--minimum-perc", action="store", dest="minimum_perc",
-        help=(
-            "Specify the minimum acceptable percentage of a translation "
-            "in order to download it."
-        )
-    )
-    parser.add_option(
-        "--mode", action="store", dest="mode", help=(
-            "Specify the mode of the translation file to pull (e.g. "
-            "'reviewed'). See http://help.transifex.com/features/client/"
-            "index.html#defining-the-mode-of-the-translated-file for the"
-            "available values."
-        )
-    )
-    group = OptionGroup(parser, "Extended options", "These options can only be"
-        " used with the --auto-local command.")
-    group.add_option("-s","--source-language", action="store",
-        dest="source_language",
-        default=None, help="Specify the source language of a resource"
-        " [requires --auto-local].")
-    group.add_option("-f","--source-file", action="store", dest="source_file",
-        default=None, help="Specify the source file of a resource [requires"
-        " --auto-local].")
-    group.add_option("--execute", action="store_true", dest="execute",
-        default=False, help="Execute commands [requires --auto-local].")
-    parser.add_option_group(group)
-    return parser
-
-
-def status_parser():
-    """Return the command-line parser for the status command."""
-    usage="usage: %prog [tx_options] status [options]"
-    description="Prints the status of the current project by reading the"\
-        " data in the configuration file."
-    parser = OptionParser(usage=usage,description=description)
-    parser.add_option("-r","--resource", action="store", dest="resources",
-        default=[], help="Specify resources")
-    return parser
-
-
-def parse_csv_option(option):
-    """Return a list out of the comma-separated option or an empty list."""
-    if option:
-        return option.split(',')
-    else:
-        return []

+ 0 - 54
third_party/transifex-client/txclib/processors.py

@@ -1,54 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-Module for API-related calls.
-"""
-
-import urlparse
-
-
-def hostname_tld_migration(hostname):
-    """
-    Migrate transifex.net to transifex.com.
-
-    :param hostname: The hostname to migrate (if needed).
-    :returns: A hostname with the transifex.com domain (if needed).
-    """
-    parts = urlparse.urlparse(hostname)
-    if parts.hostname.endswith('transifex.net'):
-        hostname = hostname.replace('transifex.net', 'transifex.com', 1)
-    return hostname
-
-
-def hostname_ssl_migration(hostname):
-    """
-    Migrate Transifex hostnames to use HTTPS.
-
-    :param hostname: The hostname to migrate (if needed).
-    :returns: A https hostname (if needed).
-    """
-    parts = urlparse.urlparse(hostname)
-    is_transifex = (
-        parts.hostname[-14:-3] == '.transifex.' or
-        parts.hostname == 'transifex.net' or
-        parts.hostname == 'transifex.com'
-    )
-    is_https = parts.scheme == 'https'
-    if is_transifex and not is_https:
-        if not parts.scheme:
-            hostname = 'https:' + hostname
-        else:
-            hostname = hostname.replace(parts.scheme, 'https', 1)
-    return hostname
-
-
-def visit_hostname(hostname):
-    """
-    Have a chance to visit a hostname before actually using it.
-
-    :param hostname: The original hostname.
-    :returns: The hostname with the necessary changes.
-    """
-    for processor in [hostname_ssl_migration, hostname_tld_migration, ]:
-        hostname = processor(hostname)
-    return hostname

+ 0 - 1233
third_party/transifex-client/txclib/project.py

@@ -1,1233 +0,0 @@
-# -*- coding: utf-8 -*-
-import base64
-import copy
-import getpass
-import os
-import re
-import fnmatch
-import urllib2
-import datetime, time
-import ConfigParser
-
-from txclib.web import *
-from txclib.utils import *
-from txclib.urls import API_URLS
-from txclib.config import OrderedRawConfigParser, Flipdict
-from txclib.log import logger
-from txclib.http_utils import http_response
-from txclib.processors import visit_hostname
-
-
-class ProjectNotInit(Exception):
-    pass
-
-
-class Project(object):
-    """
-    Represents an association between the local and remote project instances.
-    """
-
-    def __init__(self, path_to_tx=None, init=True):
-        """
-        Initialize the Project attributes.
-        """
-        if init:
-            self._init(path_to_tx)
-
-    def _init(self, path_to_tx=None):
-        instructions = "Run 'tx init' to initialize your project first!"
-        try:
-            self.root = self._get_tx_dir_path(path_to_tx)
-            self.config_file = self._get_config_file_path(self.root)
-            self.config = self._read_config_file(self.config_file)
-            self.txrc_file = self._get_transifex_file()
-            self.txrc = self._get_transifex_config(self.txrc_file)
-        except ProjectNotInit, e:
-            logger.error('\n'.join([unicode(e), instructions]))
-            raise
-
-    def _get_config_file_path(self, root_path):
-        """Check the .tx/config file exists."""
-        config_file = os.path.join(root_path, ".tx", "config")
-        logger.debug("Config file is %s" % config_file)
-        if not os.path.exists(config_file):
-            msg = "Cannot find the config file (.tx/config)!"
-            raise ProjectNotInit(msg)
-        return config_file
-
-    def _get_tx_dir_path(self, path_to_tx):
-        """Check the .tx directory exists."""
-        root_path = path_to_tx or find_dot_tx()
-        logger.debug("Path to tx is %s." % root_path)
-        if not root_path:
-            msg = "Cannot find any .tx directory!"
-            raise ProjectNotInit(msg)
-        return root_path
-
-    def _read_config_file(self, config_file):
-        """Parse the config file and return its contents."""
-        config = OrderedRawConfigParser()
-        try:
-            config.read(config_file)
-        except Exception, err:
-            msg = "Cannot open/parse .tx/config file: %s" % err
-            raise ProjectNotInit(msg)
-        return config
-
-    def _get_transifex_config(self, txrc_file):
-        """Read the configuration from the .transifexrc file."""
-        txrc = OrderedRawConfigParser()
-        try:
-            txrc.read(txrc_file)
-        except Exception, e:
-            msg = "Cannot read global configuration file: %s" % e
-            raise ProjectNotInit(msg)
-        self._migrate_txrc_file(txrc)
-        return txrc
-
-    def _migrate_txrc_file(self, txrc):
-        """Migrate the txrc file, if needed."""
-        for section in txrc.sections():
-            orig_hostname = txrc.get(section, 'hostname')
-            hostname = visit_hostname(orig_hostname)
-            if hostname != orig_hostname:
-                msg = "Hostname %s should be changed to %s."
-                logger.info(msg % (orig_hostname, hostname))
-                if (sys.stdin.isatty() and sys.stdout.isatty() and
-                    confirm('Change it now? ', default=True)):
-                    txrc.set(section, 'hostname', hostname)
-                    msg = 'Hostname changed'
-                    logger.info(msg)
-                else:
-                    hostname = orig_hostname
-            self._save_txrc_file(txrc)
-        return txrc
-
-    def _get_transifex_file(self, directory=None):
-        """Fetch the path of the .transifexrc file.
-
-        It is in the home directory ofthe user by default.
-        """
-        if directory is None:
-            directory = os.path.expanduser('~')
-        txrc_file = os.path.join(directory, ".transifexrc")
-        logger.debug(".transifexrc file is at %s" % directory)
-        if not os.path.exists(txrc_file):
-            msg = "No authentication data found."
-            logger.info(msg)
-            mask = os.umask(077)
-            open(txrc_file, 'w').close()
-            os.umask(mask)
-        return txrc_file
-
-    def validate_config(self):
-        """
-        To ensure the json structure is correctly formed.
-        """
-        pass
-
-    def getset_host_credentials(self, host, user=None, password=None):
-        """
-        Read .transifexrc and report user,pass for a specific host else ask the
-        user for input.
-        """
-        try:
-            username = self.txrc.get(host, 'username')
-            passwd = self.txrc.get(host, 'password')
-        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
-            logger.info("No entry found for host %s. Creating..." % host)
-            username = user or raw_input("Please enter your transifex username: ")
-            while (not username):
-                username = raw_input("Please enter your transifex username: ")
-            passwd = password
-            while (not passwd):
-                passwd = getpass.getpass()
-
-            logger.info("Updating %s file..." % self.txrc_file)
-            self.txrc.add_section(host)
-            self.txrc.set(host, 'username', username)
-            self.txrc.set(host, 'password', passwd)
-            self.txrc.set(host, 'token', '')
-            self.txrc.set(host, 'hostname', host)
-
-        return username, passwd
-
-    def set_remote_resource(self, resource, source_lang, i18n_type, host,
-            file_filter="translations<sep>%(proj)s.%(res)s<sep><lang>.%(extension)s"):
-        """
-        Method to handle the add/conf of a remote resource.
-        """
-        if not self.config.has_section(resource):
-            self.config.add_section(resource)
-
-        p_slug, r_slug = resource.split('.')
-        file_filter = file_filter.replace("<sep>", r"%s" % os.path.sep)
-        self.url_info = {
-            'host': host,
-            'project': p_slug,
-            'resource': r_slug
-        }
-        extension = self._extension_for(i18n_type)[1:]
-
-        self.config.set(resource, 'source_lang', source_lang)
-        self.config.set(
-            resource, 'file_filter',
-            file_filter % {'proj': p_slug, 'res': r_slug, 'extension': extension}
-        )
-        if host != self.config.get('main', 'host'):
-            self.config.set(resource, 'host', host)
-
-    def get_resource_host(self, resource):
-        """
-        Returns the host that the resource is configured to use. If there is no
-        such option we return the default one
-        """
-        if self.config.has_option(resource, 'host'):
-            return self.config.get(resource, 'host')
-        return self.config.get('main', 'host')
-
-    def get_resource_lang_mapping(self, resource):
-        """
-        Get language mappings for a specific resource.
-        """
-        lang_map = Flipdict()
-        try:
-            args = self.config.get("main", "lang_map")
-            for arg in args.replace(' ', '').split(','):
-                k,v = arg.split(":")
-                lang_map.update({k:v})
-        except ConfigParser.NoOptionError:
-            pass
-        except (ValueError, KeyError):
-            raise Exception("Your lang map configuration is not correct.")
-
-        if self.config.has_section(resource):
-            res_lang_map = Flipdict()
-            try:
-                args = self.config.get(resource, "lang_map")
-                for arg in args.replace(' ', '').split(','):
-                    k,v = arg.split(":")
-                    res_lang_map.update({k:v})
-            except ConfigParser.NoOptionError:
-                pass
-            except (ValueError, KeyError):
-                raise Exception("Your lang map configuration is not correct.")
-
-        # merge the lang maps and return result
-        lang_map.update(res_lang_map)
-
-        return lang_map
-
-
-    def get_resource_files(self, resource):
-        """
-        Get a dict for all files assigned to a resource. First we calculate the
-        files matching the file expression and then we apply all translation
-        excpetions. The resulting dict will be in this format:
-
-        { 'en': 'path/foo/en/bar.po', 'de': 'path/foo/de/bar.po', 'es': 'path/exceptions/es.po'}
-
-        NOTE: All paths are relative to the root of the project
-        """
-        tr_files = {}
-        if self.config.has_section(resource):
-            try:
-                file_filter = self.config.get(resource, "file_filter")
-            except ConfigParser.NoOptionError:
-                file_filter = "$^"
-            source_lang = self.config.get(resource, "source_lang")
-            source_file = self.get_resource_option(resource, 'source_file') or None
-            expr_re = regex_from_filefilter(file_filter, self.root)
-            expr_rec = re.compile(expr_re)
-            for root, dirs, files in os.walk(self.root):
-                for f in files:
-                    f_path = os.path.abspath(os.path.join(root, f))
-                    match = expr_rec.match(f_path)
-                    if match:
-                        lang = match.group(1)
-                        if lang != source_lang:
-                            f_path = relpath(f_path, self.root)
-                            if f_path != source_file:
-                                tr_files.update({lang: f_path})
-
-            for (name, value) in self.config.items(resource):
-                if name.startswith("trans."):
-                    lang = name.split('.')[1]
-                    # delete language which has same file
-                    if value in tr_files.values():
-                        keys = []
-                        for k, v in tr_files.iteritems():
-                            if v == value:
-                                keys.append(k)
-                        if len(keys) == 1:
-                            del tr_files[keys[0]]
-                        else:
-                            raise Exception("Your configuration seems wrong."\
-                                " You have multiple languages pointing to"\
-                                " the same file.")
-                    # Add language with correct file
-                    tr_files.update({lang:value})
-
-            return tr_files
-
-        return None
-
-    def get_resource_option(self, resource, option):
-        """
-        Return the requested option for a specific resource
-
-        If there is no such option, we return None
-        """
-
-        if self.config.has_section(resource):
-            if self.config.has_option(resource, option):
-                return self.config.get(resource, option)
-        return None
-
-    def get_resource_list(self, project=None):
-        """
-        Parse config file and return tuples with the following format
-
-        [ (project_slug, resource_slug), (..., ...)]
-        """
-
-        resource_list= []
-        for r in self.config.sections():
-            if r == 'main':
-                continue
-            p_slug, r_slug = r.split('.', 1)
-            if project and p_slug != project:
-                continue
-            resource_list.append(r)
-
-        return resource_list
-
-    def save(self):
-        """
-        Store the config dictionary in the .tx/config file of the project.
-        """
-        self._save_tx_config()
-        self._save_txrc_file()
-
-    def _save_tx_config(self, config=None):
-        """Save the local config file."""
-        if config is None:
-            config = self.config
-        fh = open(self.config_file,"w")
-        config.write(fh)
-        fh.close()
-
-    def _save_txrc_file(self, txrc=None):
-        """Save the .transifexrc file."""
-        if txrc is None:
-            txrc = self.txrc
-        mask = os.umask(077)
-        fh = open(self.txrc_file, 'w')
-        txrc.write(fh)
-        fh.close()
-        os.umask(mask)
-
-    def get_full_path(self, relpath):
-        if relpath[0] == "/":
-            return relpath
-        else:
-            return os.path.join(self.root, relpath)
-
-    def pull(self, languages=[], resources=[], overwrite=True, fetchall=False,
-        fetchsource=False, force=False, skip=False, minimum_perc=0, mode=None):
-        """Pull all translations file from transifex server."""
-        self.minimum_perc = minimum_perc
-        resource_list = self.get_chosen_resources(resources)
-
-        if mode == 'reviewed':
-            url = 'pull_reviewed_file'
-        elif mode == 'translator':
-            url = 'pull_translator_file'
-        elif mode == 'developer':
-            url = 'pull_developer_file'
-        else:
-            url = 'pull_file'
-
-        for resource in resource_list:
-            logger.debug("Handling resource %s" % resource)
-            self.resource = resource
-            project_slug, resource_slug = resource.split('.')
-            files = self.get_resource_files(resource)
-            slang = self.get_resource_option(resource, 'source_lang')
-            sfile = self.get_resource_option(resource, 'source_file')
-            lang_map = self.get_resource_lang_mapping(resource)
-            host = self.get_resource_host(resource)
-            logger.debug("Language mapping is: %s" % lang_map)
-            if mode is None:
-                mode = self._get_option(resource, 'mode')
-            self.url_info = {
-                'host': host,
-                'project': project_slug,
-                'resource': resource_slug
-            }
-            logger.debug("URL data are: %s" % self.url_info)
-
-            stats = self._get_stats_for_resource()
-
-
-            try:
-                file_filter = self.config.get(resource, 'file_filter')
-            except ConfigParser.NoOptionError:
-                file_filter = None
-
-            # Pull source file
-            pull_languages = set([])
-            new_translations = set([])
-
-            if fetchall:
-                new_translations = self._new_translations_to_add(
-                    files, slang, lang_map, stats, force
-                )
-                if new_translations:
-                    msg = "New translations found for the following languages: %s"
-                    logger.info(msg % ', '.join(new_translations))
-
-            existing, new = self._languages_to_pull(
-                languages, files, lang_map, stats, force
-            )
-            pull_languages |= existing
-            new_translations |= new
-            logger.debug("Adding to new translations: %s" % new)
-
-            if fetchsource:
-                if sfile and slang not in pull_languages:
-                    pull_languages.add(slang)
-                elif slang not in new_translations:
-                    new_translations.add(slang)
-
-            if pull_languages:
-                logger.debug("Pulling languages for: %s" % pull_languages)
-                msg = "Pulling translations for resource %s (source: %s)"
-                logger.info(msg % (resource, sfile))
-
-            for lang in pull_languages:
-                local_lang = lang
-                if lang in lang_map.values():
-                    remote_lang = lang_map.flip[lang]
-                else:
-                    remote_lang = lang
-                if languages and lang not in pull_languages:
-                    logger.debug("Skipping language %s" % lang)
-                    continue
-                if lang != slang:
-                    local_file = files.get(lang, None) or files[lang_map[lang]]
-                else:
-                    local_file = sfile
-                logger.debug("Using file %s" % local_file)
-
-                kwargs = {
-                    'lang': remote_lang,
-                    'stats': stats,
-                    'local_file': local_file,
-                    'force': force,
-                    'mode': mode,
-                }
-                if not self._should_update_translation(**kwargs):
-                    msg = "Skipping '%s' translation (file: %s)."
-                    logger.info(
-                        msg % (color_text(remote_lang, "RED"), local_file)
-                    )
-                    continue
-
-                if not overwrite:
-                    local_file = ("%s.new" % local_file)
-                logger.warning(
-                    " -> %s: %s" % (color_text(remote_lang, "RED"), local_file)
-                )
-                try:
-                    r = self.do_url_request(url, language=remote_lang)
-                except Exception,e:
-                    if not skip:
-                        raise e
-                    else:
-                        logger.error(e)
-                        continue
-                base_dir = os.path.split(local_file)[0]
-                mkdir_p(base_dir)
-                fd = open(local_file, 'wb')
-                fd.write(r)
-                fd.close()
-
-            if new_translations:
-                msg = "Pulling new translations for resource %s (source: %s)"
-                logger.info(msg % (resource, sfile))
-                for lang in new_translations:
-                    if lang in lang_map.keys():
-                        local_lang = lang_map[lang]
-                    else:
-                        local_lang = lang
-                    remote_lang = lang
-                    if file_filter:
-                        local_file = relpath(os.path.join(self.root,
-                            file_filter.replace('<lang>', local_lang)), os.curdir)
-                    else:
-                        trans_dir = os.path.join(self.root, ".tx", resource)
-                        if not os.path.exists(trans_dir):
-                            os.mkdir(trans_dir)
-                        local_file = relpath(os.path.join(trans_dir, '%s_translation' %
-                            local_lang, os.curdir))
-
-                    if lang != slang:
-                        satisfies_min = self._satisfies_min_translated(
-                            stats[remote_lang], mode
-                        )
-                        if not satisfies_min:
-                            msg = "Skipping language %s due to used options."
-                            logger.info(msg % lang)
-                            continue
-                    logger.warning(
-                        " -> %s: %s" % (color_text(remote_lang, "RED"), local_file)
-                    )
-                    r = self.do_url_request(url, language=remote_lang)
-
-                    base_dir = os.path.split(local_file)[0]
-                    mkdir_p(base_dir)
-                    fd = open(local_file, 'wb')
-                    fd.write(r)
-                    fd.close()
-
-    def push(self, source=False, translations=False, force=False, resources=[], languages=[],
-        skip=False, no_interactive=False):
-        """
-        Push all the resources
-        """
-        resource_list = self.get_chosen_resources(resources)
-        self.skip = skip
-        self.force = force
-        for resource in resource_list:
-            push_languages = []
-            project_slug, resource_slug = resource.split('.')
-            files = self.get_resource_files(resource)
-            slang = self.get_resource_option(resource, 'source_lang')
-            sfile = self.get_resource_option(resource, 'source_file')
-            lang_map = self.get_resource_lang_mapping(resource)
-            host = self.get_resource_host(resource)
-            logger.debug("Language mapping is: %s" % lang_map)
-            logger.debug("Using host %s" % host)
-            self.url_info = {
-                'host': host,
-                'project': project_slug,
-                'resource': resource_slug
-            }
-
-            logger.info("Pushing translations for resource %s:" % resource)
-
-            stats = self._get_stats_for_resource()
-
-            if force and not no_interactive:
-                answer = raw_input("Warning: By using --force, the uploaded"
-                    " files will overwrite remote translations, even if they"
-                    " are newer than your uploaded files.\nAre you sure you"
-                    " want to continue? [y/N] ")
-
-                if not answer in ["", 'Y', 'y', "yes", 'YES']:
-                    return
-
-            if source:
-                if sfile == None:
-                    logger.error("You don't seem to have a proper source file"
-                        " mapping for resource %s. Try without the --source"
-                        " option or set a source file first and then try again." %
-                        resource)
-                    continue
-                # Push source file
-                try:
-                    logger.warning("Pushing source file (%s)" % sfile)
-                    if not self._resource_exists(stats):
-                        logger.info("Resource does not exist.  Creating...")
-                        fileinfo = "%s;%s" % (resource_slug, slang)
-                        filename = self.get_full_path(sfile)
-                        self._create_resource(resource, project_slug, fileinfo, filename)
-                    self.do_url_request(
-                        'push_source', multipart=True, method="PUT",
-                        files=[(
-                                "%s;%s" % (resource_slug, slang)
-                                , self.get_full_path(sfile)
-                        )],
-                    )
-                except Exception, e:
-                    if not skip:
-                        raise
-                    else:
-                        logger.error(e)
-            else:
-                try:
-                    self.do_url_request('resource_details')
-                except Exception, e:
-                    code = getattr(e, 'code', None)
-                    if code == 404:
-                        msg = "Resource %s doesn't exist on the server."
-                        logger.error(msg % resource)
-                        continue
-
-            if translations:
-                # Check if given language codes exist
-                if not languages:
-                    push_languages = files.keys()
-                else:
-                    push_languages = []
-                    f_langs = files.keys()
-                    for l in languages:
-                        if l in lang_map.keys():
-                            l = lang_map[l]
-                        push_languages.append(l)
-                        if l not in f_langs:
-                            msg = "Warning: No mapping found for language code '%s'."
-                            logger.error(msg % color_text(l,"RED"))
-                logger.debug("Languages to push are %s" % push_languages)
-
-                # Push translation files one by one
-                for lang in push_languages:
-                    local_lang = lang
-                    if lang in lang_map.values():
-                        remote_lang = lang_map.flip[lang]
-                    else:
-                        remote_lang = lang
-
-                    local_file = files[local_lang]
-
-                    kwargs = {
-                        'lang': remote_lang,
-                        'stats': stats,
-                        'local_file': local_file,
-                        'force': force,
-                    }
-                    if not self._should_push_translation(**kwargs):
-                        msg = "Skipping '%s' translation (file: %s)."
-                        logger.info(msg % (color_text(lang, "RED"), local_file))
-                        continue
-
-                    msg = "Pushing '%s' translations (file: %s)"
-                    logger.warning(
-                         msg % (color_text(remote_lang, "RED"), local_file)
-                    )
-                    try:
-                        self.do_url_request(
-                            'push_translation', multipart=True, method='PUT',
-                            files=[(
-                                    "%s;%s" % (resource_slug, remote_lang),
-                                    self.get_full_path(local_file)
-                            )], language=remote_lang
-                        )
-                        logger.debug("Translation %s pushed." % remote_lang)
-                    except Exception, e:
-                        if not skip:
-                            raise e
-                        else:
-                            logger.error(e)
-
-    def delete(self, resources=[], languages=[], skip=False, force=False):
-        """Delete translations."""
-        resource_list = self.get_chosen_resources(resources)
-        self.skip = skip
-        self.force = force
-
-        if not languages:
-            delete_func = self._delete_resource
-        else:
-            delete_func = self._delete_translations
-
-        for resource in resource_list:
-            project_slug, resource_slug = resource.split('.')
-            host = self.get_resource_host(resource)
-            self.url_info = {
-                'host': host,
-                'project': project_slug,
-                'resource': resource_slug
-            }
-            logger.debug("URL data are: %s" % self.url_info)
-            project_details = parse_json(
-                self.do_url_request('project_details', project=self)
-            )
-            teams = project_details['teams']
-            stats = self._get_stats_for_resource()
-            delete_func(project_details, resource, stats, languages)
-
-    def _delete_resource(self, project_details, resource, stats, *args):
-        """Delete a resource from Transifex."""
-        project_slug, resource_slug = resource.split('.')
-        project_resource_slugs = [
-            r['slug'] for r in project_details['resources']
-        ]
-        logger.info("Deleting resource %s:" % resource)
-        if resource_slug not in project_resource_slugs:
-            if not self.skip:
-                msg = "Skipping: %s : Resource does not exist."
-                logger.info(msg % resource)
-            return
-        if not self.force:
-            slang = self.get_resource_option(resource, 'source_lang')
-            for language in stats:
-                if language == slang:
-                    continue
-                if int(stats[language]['translated_entities']) > 0:
-                    msg = (
-                        "Skipping: %s : Unable to delete resource because it "
-                        "has a not empty %s translation.\nPlease use -f or "
-                        "--force option to delete this resource."
-                    )
-                    logger.info(msg % (resource, language))
-                    return
-        try:
-            self.do_url_request('delete_resource', method="DELETE")
-            self.config.remove_section(resource)
-            self.save()
-            msg = "Deleted resource %s of project %s."
-            logger.info(msg % (resource_slug, project_slug))
-        except Exception, e:
-            msg = "Unable to delete resource %s of project %s."
-            logger.error(msg % (resource_slug, project_slug))
-            if not self.skip:
-                raise
-
-    def _delete_translations(self, project_details, resource, stats, languages):
-        """Delete the specified translations for the specified resource."""
-        logger.info("Deleting translations from resource %s:" % resource)
-        for language in languages:
-            self._delete_translation(project_details, resource, stats, language)
-
-    def _delete_translation(self, project_details, resource, stats, language):
-        """Delete a specific translation from the specified resource."""
-        project_slug, resource_slug = resource.split('.')
-        if language not in stats:
-            if not self.skip:
-                msg = "Skipping %s: Translation does not exist."
-                logger.warning(msg % (language))
-            return
-        if not self.force:
-            teams = project_details['teams']
-            if language in teams:
-                msg = (
-                    "Skipping %s: Unable to delete translation because it is "
-                    "associated with a team.\nPlease use -f or --force option "
-                    "to delete this translation."
-                )
-                logger.warning(msg % language)
-                return
-            if int(stats[language]['translated_entities']) > 0:
-                msg = (
-                    "Skipping %s: Unable to delete translation because it "
-                    "is not empty.\nPlease use -f or --force option to delete "
-                    "this translation."
-                )
-                logger.warning(msg % language)
-                return
-        try:
-            self.do_url_request(
-                'delete_translation', language=language, method="DELETE"
-            )
-            msg = "Deleted language %s from resource %s of project %s."
-            logger.info(msg % (language, resource_slug, project_slug))
-        except Exception, e:
-            msg = "Unable to delete translation %s"
-            logger.error(msg % language)
-            if not self.skip:
-                raise
-
-    def do_url_request(self, api_call, multipart=False, data=None,
-                       files=[], encoding=None, method="GET", **kwargs):
-        """
-        Issues a url request.
-        """
-        # Read the credentials from the config file (.transifexrc)
-        host = self.url_info['host']
-        try:
-            username = self.txrc.get(host, 'username')
-            passwd = self.txrc.get(host, 'password')
-            token = self.txrc.get(host, 'token')
-            hostname = self.txrc.get(host, 'hostname')
-        except ConfigParser.NoSectionError:
-            raise Exception("No user credentials found for host %s. Edit"
-                " ~/.transifexrc and add the appropriate info in there." %
-                host)
-
-        # Create the Url
-        kwargs['hostname'] = hostname
-        kwargs.update(self.url_info)
-        url = (API_URLS[api_call] % kwargs).encode('UTF-8')
-        logger.debug(url)
-
-        opener = None
-        headers = None
-        req = None
-
-        if multipart:
-            opener = urllib2.build_opener(MultipartPostHandler)
-            for info,filename in files:
-                data = { "resource" : info.split(';')[0],
-                         "language" : info.split(';')[1],
-                         "uploaded_file" :  open(filename,'rb') }
-
-            urllib2.install_opener(opener)
-            req = RequestWithMethod(url=url, data=data, method=method)
-        else:
-            req = RequestWithMethod(url=url, data=data, method=method)
-            if encoding:
-                req.add_header("Content-Type",encoding)
-
-        base64string = base64.encodestring('%s:%s' % (username, passwd))[:-1]
-        authheader = "Basic %s" % base64string
-        req.add_header("Authorization", authheader)
-        req.add_header("Accept-Encoding", "gzip,deflate")
-        req.add_header("User-Agent", user_agent_identifier())
-
-        try:
-            response = urllib2.urlopen(req, timeout=300)
-            return http_response(response)
-        except urllib2.HTTPError, e:
-            if e.code in [401, 403, 404]:
-                raise e
-            elif 200 <= e.code < 300:
-                return None
-            else:
-                # For other requests, we should print the message as well
-                raise Exception("Remote server replied: %s" % e.read())
-        except urllib2.URLError, e:
-            error = e.args[0]
-            raise Exception("Remote server replied: %s" % error[1])
-
-
-    def _should_update_translation(self, lang, stats, local_file, force=False,
-                                   mode=None):
-        """Whether a translation should be udpated from Transifex.
-
-        We use the following criteria for that:
-        - If user requested to force the download.
-        - If language exists in Transifex.
-        - If the local file is older than the Transifex's file.
-        - If the user requested a x% completion.
-
-        Args:
-            lang: The language code to check.
-            stats: The (global) statistics object.
-            local_file: The local translation file.
-            force: A boolean flag.
-            mode: The mode for the translation.
-        Returns:
-            True or False.
-        """
-        return self._should_download(lang, stats, local_file, force)
-
-    def _should_add_translation(self, lang, stats, force=False, mode=None):
-        """Whether a translation should be added from Transifex.
-
-        We use the following criteria for that:
-        - If user requested to force the download.
-        - If language exists in Transifex.
-        - If the user requested a x% completion.
-
-        Args:
-            lang: The language code to check.
-            stats: The (global) statistics object.
-            force: A boolean flag.
-            mode: The mode for the translation.
-        Returns:
-            True or False.
-        """
-        return self._should_download(lang, stats, None, force)
-
-    def _should_download(self, lang, stats, local_file=None, force=False,
-                         mode=None):
-        """Return whether a translation should be downloaded.
-
-        If local_file is None, skip the timestamps check (the file does
-        not exist locally).
-        """
-        try:
-            lang_stats = stats[lang]
-        except KeyError, e:
-            logger.debug("No lang %s in statistics" % lang)
-            return False
-
-        satisfies_min = self._satisfies_min_translated(lang_stats, mode)
-        if not satisfies_min:
-            return False
-
-        if force:
-            logger.debug("Downloading translation due to -f")
-            return True
-
-        if local_file is not None:
-            remote_update = self._extract_updated(lang_stats)
-            if not self._remote_is_newer(remote_update, local_file):
-                logger.debug("Local is newer than remote for lang %s" % lang)
-                return False
-        return True
-
-    def _should_push_translation(self, lang, stats, local_file, force=False):
-        """Return whether a local translation file should be
-        pushed to Trasnifex.
-
-        We use the following criteria for that:
-        - If user requested to force the upload.
-        - If language exists in Transifex.
-        - If local file is younger than the remote file.
-
-        Args:
-            lang: The language code to check.
-            stats: The (global) statistics object.
-            local_file: The local translation file.
-            force: A boolean flag.
-        Returns:
-            True or False.
-        """
-        if force:
-            logger.debug("Push translation due to -f.")
-            return True
-        try:
-            lang_stats = stats[lang]
-        except KeyError, e:
-            logger.debug("Language %s does not exist in Transifex." % lang)
-            return True
-        if local_file is not None:
-            remote_update = self._extract_updated(lang_stats)
-            if self._remote_is_newer(remote_update, local_file):
-                msg  = "Remote translation is newer than local file for lang %s"
-                logger.debug(msg % lang)
-                return False
-        return True
-
-    def _generate_timestamp(self, update_datetime):
-        """Generate a UNIX timestamp from the argument.
-
-        Args:
-            update_datetime: The datetime in the format used by Transifex.
-        Returns:
-            A float, representing the timestamp that corresponds to the
-            argument.
-        """
-        time_format = "%Y-%m-%d %H:%M:%S"
-        return time.mktime(
-            datetime.datetime(
-                *time.strptime(update_datetime, time_format)[0:5]
-            ).utctimetuple()
-        )
-
-    def _get_time_of_local_file(self, path):
-        """Get the modified time of the path_.
-
-        Args:
-            path: The path we want the mtime for.
-        Returns:
-            The time as a timestamp or None, if the file does not exist
-        """
-        if not os.path.exists(path):
-            return None
-        return time.mktime(time.gmtime(os.path.getmtime(path)))
-
-    def _satisfies_min_translated(self, stats, mode=None):
-        """Check whether a translation fulfills the filter used for
-        minimum translated percentage.
-
-        Args:
-            perc: The current translation percentage.
-        Returns:
-            True or False
-        """
-        cur = self._extract_completed(stats, mode)
-        option_name = 'minimum_perc'
-        if self.minimum_perc is not None:
-            minimum_percent = self.minimum_perc
-        else:
-            global_minimum = int(
-                self.get_resource_option('main', option_name) or 0
-            )
-            resource_minimum = int(
-                self.get_resource_option(
-                    self.resource, option_name
-                ) or global_minimum
-            )
-            minimum_percent = resource_minimum
-        return cur >= minimum_percent
-
-    def _remote_is_newer(self, remote_updated, local_file):
-        """Check whether the remote translation is newer that the local file.
-
-        Args:
-            remote_updated: The date and time the translation was last
-                updated remotely.
-            local_file: The local file.
-        Returns:
-            True or False.
-        """
-        if remote_updated is None:
-            logger.debug("No remote time")
-            return False
-        remote_time = self._generate_timestamp(remote_updated)
-        local_time = self._get_time_of_local_file(
-            self.get_full_path(local_file)
-        )
-        logger.debug(
-            "Remote time is %s and local %s" % (remote_time, local_time)
-        )
-        if local_time is not None and remote_time < local_time:
-            return False
-        return True
-
-    @classmethod
-    def _extract_completed(cls, stats, mode=None):
-        """Extract the information for the translated percentage from the stats.
-
-        Args:
-            stats: The stats object for a language as returned by Transifex.
-            mode: The mode of translations requested.
-        Returns:
-            The percentage of translation as integer.
-        """
-        if mode == 'reviewed':
-            key = 'reviewed_percentage'
-        else:
-            key = 'completed'
-        try:
-            return int(stats[key][:-1])
-        except KeyError, e:
-            return 0
-
-    @classmethod
-    def _extract_updated(cls, stats):
-        """Extract the  information for the last update of a translation.
-
-        Args:
-            stats: The stats object for a language as returned by Transifex.
-        Returns:
-            The last update field.
-        """
-        try:
-            return stats['last_update']
-        except KeyError, e:
-            return None
-
-    def _new_translations_to_add(self, files, slang, lang_map,
-                                 stats, force=False):
-        """Return a list of translations which are new to the
-        local installation.
-        """
-        new_translations = []
-        timestamp = time.time()
-        langs = stats.keys()
-        logger.debug("Available languages are: %s" % langs)
-
-        for lang in langs:
-            lang_exists = lang in files.keys()
-            lang_is_source = lang == slang
-            mapped_lang_exists = (
-                lang in lang_map and lang_map[lang] in files.keys()
-            )
-            if lang_exists or lang_is_source or mapped_lang_exists:
-                continue
-            if self._should_add_translation(lang, stats, force):
-                new_translations.append(lang)
-        return set(new_translations)
-
-    def _get_stats_for_resource(self):
-        """Get the statistics information for a resource."""
-        try:
-            r = self.do_url_request('resource_stats')
-            logger.debug("Statistics response is %s" % r)
-            stats = parse_json(r)
-        except urllib2.HTTPError, e:
-            logger.debug("Resource not found: %s" % e)
-            stats = {}
-        except Exception,e:
-            logger.debug("Network error: %s" % e)
-            raise
-        return stats
-
-    def get_chosen_resources(self, resources):
-        """Get the resources the user selected.
-
-        Support wildcards in the resources specified by the user.
-
-        Args:
-            resources: A list of resources as specified in command-line or
-                an empty list.
-        Returns:
-            A list of resources.
-        """
-        configured_resources = self.get_resource_list()
-        if not resources:
-            return configured_resources
-
-        selected_resources = []
-        for resource in resources:
-            found = False
-            for full_name in configured_resources:
-                if fnmatch.fnmatch(full_name, resource):
-                    selected_resources.append(full_name)
-                    found = True
-            if not found:
-                msg = "Specified resource '%s' does not exist."
-                raise Exception(msg % resource)
-        logger.debug("Operating on resources: %s" % selected_resources)
-        return selected_resources
-
-    def _languages_to_pull(self, languages, files, lang_map, stats, force):
-        """Get a set of langauges to pull.
-
-        Args:
-            languages: A list of languages the user selected in cmd.
-            files: A dictionary of current local translation files.
-        Returns:
-            A tuple of a set of existing languages and new translations.
-        """
-        if not languages:
-            pull_languages = set([])
-            pull_languages |= set(files.keys())
-            mapped_files = []
-            for lang in pull_languages:
-                if lang in lang_map.flip:
-                    mapped_files.append(lang_map.flip[lang])
-            pull_languages -= set(lang_map.flip.keys())
-            pull_languages |= set(mapped_files)
-            return (pull_languages, set([]))
-        else:
-            pull_languages = []
-            new_translations = []
-            f_langs = files.keys()
-            for l in languages:
-                if l not in f_langs and not (l in lang_map and lang_map[l] in f_langs):
-                    if self._should_add_translation(l, stats, force):
-                        new_translations.append(l)
-                else:
-                    if l in lang_map.keys():
-                        l = lang_map[l]
-                    pull_languages.append(l)
-            return (set(pull_languages), set(new_translations))
-
-    def _extension_for(self, i18n_type):
-        """Return the extension used for the specified type."""
-        try:
-            res = parse_json(self.do_url_request('formats'))
-            return res[i18n_type]['file-extensions'].split(',')[0]
-        except Exception,e:
-            logger.error(e)
-            return ''
-
-    def _resource_exists(self, stats):
-        """Check if resource exists.
-
-        Args:
-            stats: The statistics dict as returned by Tx.
-        Returns:
-            True, if the resource exists in the server.
-        """
-        return bool(stats)
-
-    def _create_resource(self, resource, pslug, fileinfo, filename, **kwargs):
-        """Create a resource.
-
-        Args:
-            resource: The full resource name.
-            pslug: The slug of the project.
-            fileinfo: The information of the resource.
-            filename: The name of the file.
-        Raises:
-            URLError, in case of a problem.
-        """
-        multipart = True
-        method = "POST"
-        api_call = 'create_resource'
-
-        host = self.url_info['host']
-        try:
-            username = self.txrc.get(host, 'username')
-            passwd = self.txrc.get(host, 'password')
-            token = self.txrc.get(host, 'token')
-            hostname = self.txrc.get(host, 'hostname')
-        except ConfigParser.NoSectionError:
-            raise Exception("No user credentials found for host %s. Edit"
-                " ~/.transifexrc and add the appropriate info in there." %
-                host)
-
-        # Create the Url
-        kwargs['hostname'] = hostname
-        kwargs.update(self.url_info)
-        kwargs['project'] = pslug
-        url = (API_URLS[api_call] % kwargs).encode('UTF-8')
-
-        opener = None
-        headers = None
-        req = None
-
-        i18n_type = self._get_option(resource, 'type')
-        if i18n_type is None:
-            logger.error(
-                "Please define the resource type in .tx/config (eg. type = PO)."
-                " More info: http://bit.ly/txcl-rt"
-            )
-
-        opener = urllib2.build_opener(MultipartPostHandler)
-        data = {
-            "slug": fileinfo.split(';')[0],
-            "name": fileinfo.split(';')[0],
-            "uploaded_file":  open(filename,'rb'),
-            "i18n_type": i18n_type
-        }
-        urllib2.install_opener(opener)
-        req = RequestWithMethod(url=url, data=data, method=method)
-
-        base64string = base64.encodestring('%s:%s' % (username, passwd))[:-1]
-        authheader = "Basic %s" % base64string
-        req.add_header("Authorization", authheader)
-
-        try:
-            fh = urllib2.urlopen(req)
-        except urllib2.HTTPError, e:
-            if e.code in [401, 403, 404]:
-                raise e
-            else:
-                # For other requests, we should print the message as well
-                raise Exception("Remote server replied: %s" % e.read())
-        except urllib2.URLError, e:
-            error = e.args[0]
-            raise Exception("Remote server replied: %s" % error[1])
-
-        raw = fh.read()
-        fh.close()
-        return raw
-
-    def _get_option(self, resource, option):
-        """Get the value for the option in the config file.
-
-        If the option is not in the resource section, look for it in
-        the project.
-
-        Args:
-            resource: The resource name.
-            option: The option the value of which we are interested in.
-        Returns:
-            The option value or None, if it does not exist.
-        """
-        value = self.get_resource_option(resource, option)
-        if value is None:
-            if self.config.has_option('main', option):
-                return self.config.get('main', option)
-        return value
-
-    def set_i18n_type(self, resources, i18n_type):
-        """Set the type for the specified resources."""
-        self._set_resource_option(resources, key='type', value=i18n_type)
-
-    def set_min_perc(self, resources, perc):
-        """Set the minimum percentage for the resources."""
-        self._set_resource_option(resources, key='minimum_perc', value=perc)
-
-    def set_default_mode(self, resources, mode):
-        """Set the default mode for the specified resources."""
-        self._set_resource_option(resources, key='mode', value=mode)
-
-    def _set_resource_option(self, resources, key, value):
-        """Set options in the config file.
-
-        If resources is empty. set the option globally.
-        """
-        if not resources:
-            self.config.set('main', key, value)
-            return
-        for r in resources:
-            self.config.set(r, key, value)

+ 0 - 21
third_party/transifex-client/txclib/urls.py

@@ -1,21 +0,0 @@
-# These are the Transifex API urls
-
-API_URLS = {
-    'get_resources': '%(hostname)s/api/2/project/%(project)s/resources/',
-    'project_details': '%(hostname)s/api/2/project/%(project)s/?details',
-    'resource_details': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/',
-    'release_details': '%(hostname)s/api/2/project/%(project)s/release/%(release)s/',
-    'pull_file': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/?file',
-    'pull_reviewed_file': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/?file&mode=reviewed',
-    'pull_translator_file': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/?file&mode=translated',
-    'pull_developer_file': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/?file&mode=default',
-    'resource_stats': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/stats/',
-    'create_resource': '%(hostname)s/api/2/project/%(project)s/resources/',
-    'push_source': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/content/',
-    'push_translation': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/',
-    'delete_translation': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/',
-    'formats': '%(hostname)s/api/2/formats/',
-    'delete_resource': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/',
-}
-
-

+ 0 - 259
third_party/transifex-client/txclib/utils.py

@@ -1,259 +0,0 @@
-import os, sys, re, errno
-try:
-    from json import loads as parse_json, dumps as compile_json
-except ImportError:
-    from simplejson import loads as parse_json, dumps as compile_json
-import urllib2 # This should go and instead use do_url_request everywhere
-
-from urls import API_URLS
-from txclib.log import logger
-from txclib.exceptions import UnknownCommandError
-
-
-def find_dot_tx(path = os.path.curdir, previous = None):
-    """
-    Return the path where .tx folder is found.
-
-    The 'path' should be a DIRECTORY.
-    This process is functioning recursively from the current directory to each
-    one of the ancestors dirs.
-    """
-    path = os.path.abspath(path)
-    if path == previous:
-        return None
-    joined = os.path.join(path, ".tx")
-    if os.path.isdir(joined):
-        return path
-    else:
-        return find_dot_tx(os.path.dirname(path), path)
-
-
-#################################################
-# Parse file filter expressions and create regex
-
-def regex_from_filefilter(file_filter, root_path = os.path.curdir):
-    """
-    Create proper regex from <lang> expression
-    """
-    # Force expr to be a valid regex expr (escaped) but keep <lang> intact
-    expr_re = re.escape(os.path.join(root_path, file_filter))
-    expr_re = expr_re.replace("\\<lang\\>", '<lang>').replace(
-        '<lang>', '([^%(sep)s]+)' % { 'sep': re.escape(os.path.sep)})
-
-    return "^%s$" % expr_re
-
-
-TX_URLS = {
-    'resource': '(?P<hostname>https?://(\w|\.|:|-)+)/projects/p/(?P<project>(\w|-)+)/resource/(?P<resource>(\w|-)+)/?$',
-    'release': '(?P<hostname>https?://(\w|\.|:|-)+)/projects/p/(?P<project>(\w|-)+)/r/(?P<release>(\w|-)+)/?$',
-    'project': '(?P<hostname>https?://(\w|\.|:|-)+)/projects/p/(?P<project>(\w|-)+)/?$',
-}
-
-
-def parse_tx_url(url):
-    """
-    Try to match given url to any of the valid url patterns specified in
-    TX_URLS. If not match is found, we raise exception
-    """
-    for type in TX_URLS.keys():
-        pattern = TX_URLS[type]
-        m = re.match(pattern, url)
-        if m:
-            return type, m.groupdict()
-
-    raise Exception("tx: Malformed url given. Please refer to our docs: http://bit.ly/txautor")
-
-
-def get_details(api_call, username, password, *args, **kwargs):
-    """
-    Get the tx project info through the API.
-
-    This function can also be used to check the existence of a project.
-    """
-    import base64
-    url = (API_URLS[api_call] % (kwargs)).encode('UTF-8')
-
-    req = urllib2.Request(url=url)
-    base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
-    authheader = "Basic %s" % base64string
-    req.add_header("Authorization", authheader)
-
-    try:
-        fh = urllib2.urlopen(req)
-        raw = fh.read()
-        fh.close()
-        remote_project = parse_json(raw)
-    except urllib2.HTTPError, e:
-        if e.code in [401, 403, 404]:
-            raise e
-        else:
-            # For other requests, we should print the message as well
-            raise Exception("Remote server replied: %s" % e.read())
-    except urllib2.URLError, e:
-        error = e.args[0]
-        raise Exception("Remote server replied: %s" % error[1])
-
-    return remote_project
-
-
-def valid_slug(slug):
-    """
-    Check if a slug contains only valid characters.
-
-    Valid chars include [-_\w]
-    """
-    try:
-        a, b = slug.split('.')
-    except ValueError:
-        return False
-    else:
-        if re.match("^[A-Za-z0-9_-]*$", a) and re.match("^[A-Za-z0-9_-]*$", b):
-            return True
-        return False
-
-
-def discover_commands():
-    """
-    Inspect commands.py and find all available commands
-    """
-    import inspect
-    from txclib import commands
-
-    command_table = {}
-    fns = inspect.getmembers(commands, inspect.isfunction)
-
-    for name, fn in fns:
-        if name.startswith("cmd_"):
-            command_table.update({
-                name.split("cmd_")[1]:fn
-            })
-
-    return command_table
-
-
-def exec_command(command, *args, **kwargs):
-    """
-    Execute given command
-    """
-    commands = discover_commands()
-    try:
-        cmd_fn = commands[command]
-    except KeyError:
-        raise UnknownCommandError
-    cmd_fn(*args,**kwargs)
-
-
-def mkdir_p(path):
-    try:
-        if path:
-            os.makedirs(path)
-    except OSError, exc: # Python >2.5
-        if exc.errno == errno.EEXIST:
-            pass
-        else:
-            raise
-
-
-def confirm(prompt='Continue?', default=True):
-    """
-    Prompt the user for a Yes/No answer.
-
-    Args:
-        prompt: The text displayed to the user ([Y/n] will be appended)
-        default: If the default value will be yes or no
-    """
-    valid_yes = ['Y', 'y', 'Yes', 'yes', ]
-    valid_no = ['N', 'n', 'No', 'no', ]
-    if default:
-        prompt = prompt + '[Y/n]'
-        valid_yes.append('')
-    else:
-        prompt = prompt + '[y/N]'
-        valid_no.append('')
-
-    ans = raw_input(prompt)
-    while (ans not in valid_yes and ans not in valid_no):
-        ans = raw_input(prompt)
-
-    return ans in valid_yes
-
-
-# Stuff for command line colored output
-
-COLORS = [
-    'BLACK', 'RED', 'GREEN', 'YELLOW',
-    'BLUE', 'MAGENTA', 'CYAN', 'WHITE'
-]
-
-DISABLE_COLORS = False
-
-
-def color_text(text, color_name, bold=False):
-    """
-    This command can be used to colorify command line output. If the shell
-    doesn't support this or the --disable-colors options has been set, it just
-    returns the plain text.
-
-    Usage:
-        print "%s" % color_text("This text is red", "RED")
-    """
-    if color_name in COLORS and not DISABLE_COLORS:
-        return '\033[%s;%sm%s\033[0m' % (
-            int(bold), COLORS.index(color_name) + 30, text)
-    else:
-        return text
-
-
-##############################################
-# relpath implementation taken from Python 2.7
-
-if not hasattr(os.path, 'relpath'):
-    if os.path is sys.modules.get('ntpath'):
-        def relpath(path, start=os.path.curdir):
-            """Return a relative version of a path"""
-
-            if not path:
-                raise ValueError("no path specified")
-            start_list = os.path.abspath(start).split(os.path.sep)
-            path_list = os.path.abspath(path).split(os.path.sep)
-            if start_list[0].lower() != path_list[0].lower():
-                unc_path, rest = os.path.splitunc(path)
-                unc_start, rest = os.path.splitunc(start)
-                if bool(unc_path) ^ bool(unc_start):
-                    raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
-                                                                        % (path, start))
-                else:
-                    raise ValueError("path is on drive %s, start on drive %s"
-                                                        % (path_list[0], start_list[0]))
-            # Work out how much of the filepath is shared by start and path.
-            for i in range(min(len(start_list), len(path_list))):
-                if start_list[i].lower() != path_list[i].lower():
-                    break
-            else:
-                i += 1
-
-            rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
-            if not rel_list:
-                return os.path.curdir
-            return os.path.join(*rel_list)
-
-    else:
-        # default to posixpath definition
-        def relpath(path, start=os.path.curdir):
-            """Return a relative version of a path"""
-
-            if not path:
-                raise ValueError("no path specified")
-
-            start_list = os.path.abspath(start).split(os.path.sep)
-            path_list = os.path.abspath(path).split(os.path.sep)
-
-            # Work out how much of the filepath is shared by start and path.
-            i = len(os.path.commonprefix([start_list, path_list]))
-
-            rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
-            if not rel_list:
-                return os.path.curdir
-            return os.path.join(*rel_list)
-else:
-    from os.path import relpath

+ 0 - 94
third_party/transifex-client/txclib/web.py

@@ -1,94 +0,0 @@
-# -*- coding: utf-8 -*-
-import urllib2
-import itertools, mimetools, mimetypes
-import platform
-from txclib import get_version
-
-# Helper class to enable urllib2 to handle PUT/DELETE requests as well
-class RequestWithMethod(urllib2.Request):
-    """Workaround for using DELETE with urllib2"""
-    def __init__(self, url, method, data=None, headers={},
-        origin_req_host=None, unverifiable=False):
-        self._method = method
-        urllib2.Request.__init__(self, url, data=data, headers=headers,
-                 origin_req_host=None, unverifiable=False)
-
-    def get_method(self):
-        return self._method
-
-import urllib
-import os, stat
-from cStringIO import StringIO
-
-class Callable:
-    def __init__(self, anycallable):
-        self.__call__ = anycallable
-
-# Controls how sequences are uncoded. If true, elements may be given multiple
-# values by assigning a sequence.
-doseq = 1
-
-class MultipartPostHandler(urllib2.BaseHandler):
-    handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
-
-    def http_request(self, request):
-        data = request.get_data()
-        if data is not None and type(data) != str:
-            v_files = []
-            v_vars = []
-            try:
-                 for(key, value) in data.items():
-                     if type(value) == file:
-                         v_files.append((key, value))
-                     else:
-                         v_vars.append((key, value))
-            except TypeError:
-                systype, value, traceback = sys.exc_info()
-                raise TypeError, "not a valid non-string sequence or mapping object", traceback
-
-            if len(v_files) == 0:
-                data = urllib.urlencode(v_vars, doseq)
-            else:
-                boundary, data = self.multipart_encode(v_vars, v_files)
-
-                contenttype = 'multipart/form-data; boundary=%s' % boundary
-                if(request.has_header('Content-Type')
-                   and request.get_header('Content-Type').find('multipart/form-data') != 0):
-                    print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
-                request.add_unredirected_header('Content-Type', contenttype)
-
-            request.add_data(data)
-
-        return request
-
-    def multipart_encode(vars, files, boundary = None, buf = None):
-        if boundary is None:
-            boundary = mimetools.choose_boundary()
-        if buf is None:
-            buf = StringIO()
-        for(key, value) in vars:
-            buf.write('--%s\r\n' % boundary)
-            buf.write('Content-Disposition: form-data; name="%s"' % key)
-            buf.write('\r\n\r\n' + value + '\r\n')
-        for(key, fd) in files:
-            file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
-            filename = fd.name.split('/')[-1]
-            contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
-            buf.write('--%s\r\n' % boundary)
-            buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
-            buf.write('Content-Type: %s\r\n' % contenttype)
-            # buffer += 'Content-Length: %s\r\n' % file_size
-            fd.seek(0)
-            buf.write('\r\n' + fd.read() + '\r\n')
-        buf.write('--' + boundary + '--\r\n\r\n')
-        buf = buf.getvalue()
-        return boundary, buf
-    multipart_encode = Callable(multipart_encode)
-
-    https_request = http_request
-
-
-def user_agent_identifier():
-    """Return the user agent for the client."""
-    client_info = (get_version(), platform.system(), platform.machine())
-    return "txclient/%s (%s %s)" % client_info