Brion VIBBER has submitted this change and it was merged.

Change subject: Anonymous editing killswitch.
......................................................................


Anonymous editing killswitch.

Syncs with config/ios.json at most once a day. Adjusts saving
interface to hide anon saving option if ios.json has flag for
disabling anon editing.

The periodic sync code is done in generic fashion to make
future sync between server and bundled app json files
painless.

Note: will need to amend copyBundledFolderToAppDataDocuments
to copy files and folders which don't exist in documents
directory after initial install. Right now it won't copy
if it sees the Json folder has already been copied from the
bundle to the writable documents dir.

Will also need to rename all of the "bundled" things from
this commit as they're not really bundled now that they've
been moved to the documents directory.

Change-Id: Ia64c060806c14cbf08cacb3168af8f471c8de048
---
M Wikipedia.xcodeproj/project.pbxproj
M wikipedia/AppDelegate.m
A wikipedia/BundledJson/BundledJson.h
A wikipedia/BundledJson/BundledJson.m
A wikipedia/BundledJson/BundledJsonEnum.h
A wikipedia/BundledPaths/BundledPaths.h
A wikipedia/BundledPaths/BundledPaths.m
A wikipedia/BundledPaths/BundledPathsEnum.h
A wikipedia/Data/Operations/ConfigFileSyncOp.h
A wikipedia/Data/Operations/ConfigFileSyncOp.m
M wikipedia/Data/Operations/DownloadLangLinksOp.h
M wikipedia/Data/Operations/DownloadLangLinksOp.m
R wikipedia/Json/Languages/languages.json
R wikipedia/Json/Languages/mainpages.json
A wikipedia/Json/config/ios.json
M wikipedia/Queues/QueuesSingleton.h
M wikipedia/Queues/QueuesSingleton.m
M wikipedia/Session/SessionSingleton.h
M wikipedia/Session/SessionSingleton.m
M wikipedia/View Controllers/Languages/LanguagesTableVC.m
M wikipedia/View Controllers/Preview/PreviewChoicesMenuView.m
M wikipedia/View Controllers/Preview/PreviewChoicesMenuView.xib
M wikipedia/View Controllers/TopNav/NavController.m
M wikipedia/View Controllers/WebView/WebViewController.m
24 files changed, 414 insertions(+), 49 deletions(-)

Approvals:
  Brion VIBBER: Verified; Looks good to me, approved



diff --git a/Wikipedia.xcodeproj/project.pbxproj 
b/Wikipedia.xcodeproj/project.pbxproj
index 262a856..cc69069 100644
--- a/Wikipedia.xcodeproj/project.pbxproj
+++ b/Wikipedia.xcodeproj/project.pbxproj
@@ -43,6 +43,10 @@
                043DAC4B1901C3EE001CD17C /* CreditsViewController.m in Sources 
*/ = {isa = PBXBuildFile; fileRef = 043DAC4A1901C3EE001CD17C /* 
CreditsViewController.m */; };
                043F18E118D9691D00D8489A /* TopActionSheetLabel.m in Sources */ 
= {isa = PBXBuildFile; fileRef = 043F18DC18D9691D00D8489A /* 
TopActionSheetLabel.m */; };
                043F18E518D9691D00D8489A /* 
UINavigationController+TopActionSheet.m in Sources */ = {isa = PBXBuildFile; 
fileRef = 043F18E018D9691D00D8489A /* UINavigationController+TopActionSheet.m 
*/; };
+               044213C8191C3C2A006C03BF /* ConfigFileSyncOp.m in Sources */ = 
{isa = PBXBuildFile; fileRef = 044213C7191C3C2A006C03BF /* ConfigFileSyncOp.m 
*/; };
+               044213D0191D6F43006C03BF /* BundledPaths.m in Sources */ = {isa 
= PBXBuildFile; fileRef = 044213CF191D6F43006C03BF /* BundledPaths.m */; };
+               044213D4191D70E9006C03BF /* BundledJson.m in Sources */ = {isa 
= PBXBuildFile; fileRef = 044213D3191D70E9006C03BF /* BundledJson.m */; };
+               044213D8191D99FB006C03BF /* Json in Resources */ = {isa = 
PBXBuildFile; fileRef = 044213D7191D99FB006C03BF /* Json */; };
                0442F57B19006DCC00F55DF9 /* PageHistoryLabel.m in Sources */ = 
{isa = PBXBuildFile; fileRef = 0442F57A19006DCC00F55DF9 /* PageHistoryLabel.m 
*/; };
                0442F57E190071A100F55DF9 /* WikiFont.ttf in Resources */ = {isa 
= PBXBuildFile; fileRef = 0442F57D190071A100F55DF9 /* WikiFont.ttf */; };
                0447862F185145090050563B /* HistoryResultCell.m in Sources */ = 
{isa = PBXBuildFile; fileRef = 04478621185145090050563B /* HistoryResultCell.m 
*/; };
@@ -107,7 +111,6 @@
                04C695CE18ED08D900D9F2DA /* UIView+SearchSubviews.m in Sources 
*/ = {isa = PBXBuildFile; fileRef = 04C695CD18ED08D900D9F2DA /* 
UIView+SearchSubviews.m */; };
                04C695D218ED213000D9F2DA /* 
UIScrollView+NoHorizontalScrolling.m in Sources */ = {isa = PBXBuildFile; 
fileRef = 04C695D118ED213000D9F2DA /* UIScrollView+NoHorizontalScrolling.m */; 
};
                04C8781018F4A42700FA3B99 /* AccountCreationTokenOp.m in Sources 
*/ = {isa = PBXBuildFile; fileRef = 04C8780F18F4A42700FA3B99 /* 
AccountCreationTokenOp.m */; };
-               04CF1CB6187C8F4400E9516F /* Languages in Resources */ = {isa = 
PBXBuildFile; fileRef = 04CF1CB5187C8F4400E9516F /* Languages */; };
                04D122321899B8AC006B9A30 /* AlertWebView.m in Sources */ = {isa 
= PBXBuildFile; fileRef = 04D122311899B8AC006B9A30 /* AlertWebView.m */; };
                04D149DD18877343006B4104 /* AlertLabel.m in Sources */ = {isa = 
PBXBuildFile; fileRef = 04D149DA18877343006B4104 /* AlertLabel.m */; };
                04D149DF18877343006B4104 /* UIViewController+Alert.m in Sources 
*/ = {isa = PBXBuildFile; fileRef = 04D149DC18877343006B4104 /* 
UIViewController+Alert.m */; };
@@ -230,6 +233,15 @@
                043F18DF18D9691D00D8489A /* 
UINavigationController+TopActionSheet.h */ = {isa = PBXFileReference; 
fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
"UINavigationController+TopActionSheet.h"; sourceTree = "<group>"; };
                043F18E018D9691D00D8489A /* 
UINavigationController+TopActionSheet.m */ = {isa = PBXFileReference; 
fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = 
"UINavigationController+TopActionSheet.m"; sourceTree = "<group>"; };
                043F18F118DCDD3A00D8489A /* WMF_Colors.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
WMF_Colors.h; sourceTree = "<group>"; };
+               044213C6191C3C2A006C03BF /* ConfigFileSyncOp.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
ConfigFileSyncOp.h; sourceTree = "<group>"; };
+               044213C7191C3C2A006C03BF /* ConfigFileSyncOp.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= ConfigFileSyncOp.m; sourceTree = "<group>"; };
+               044213CE191D6F43006C03BF /* BundledPaths.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
BundledPaths.h; sourceTree = "<group>"; };
+               044213CF191D6F43006C03BF /* BundledPaths.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= BundledPaths.m; sourceTree = "<group>"; };
+               044213D2191D70E9006C03BF /* BundledJson.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
BundledJson.h; sourceTree = "<group>"; };
+               044213D3191D70E9006C03BF /* BundledJson.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= BundledJson.m; sourceTree = "<group>"; };
+               044213D5191D7FEA006C03BF /* BundledJsonEnum.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
BundledJsonEnum.h; sourceTree = "<group>"; };
+               044213D6191D7FFC006C03BF /* BundledPathsEnum.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
BundledPathsEnum.h; sourceTree = "<group>"; };
+               044213D7191D99FB006C03BF /* Json */ = {isa = PBXFileReference; 
lastKnownFileType = folder; path = Json; sourceTree = "<group>"; };
                0442F57919006DCC00F55DF9 /* PageHistoryLabel.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
PageHistoryLabel.h; sourceTree = "<group>"; };
                0442F57A19006DCC00F55DF9 /* PageHistoryLabel.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= PageHistoryLabel.m; sourceTree = "<group>"; };
                0442F57D190071A100F55DF9 /* WikiFont.ttf */ = {isa = 
PBXFileReference; lastKnownFileType = file; path = WikiFont.ttf; sourceTree = 
"<group>"; };
@@ -353,7 +365,6 @@
                04C695D118ED213000D9F2DA /* 
UIScrollView+NoHorizontalScrolling.m */ = {isa = PBXFileReference; fileEncoding 
= 4; lastKnownFileType = sourcecode.c.objc; path = 
"UIScrollView+NoHorizontalScrolling.m"; sourceTree = "<group>"; };
                04C8780E18F4A42700FA3B99 /* AccountCreationTokenOp.h */ = {isa 
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path 
= AccountCreationTokenOp.h; sourceTree = "<group>"; };
                04C8780F18F4A42700FA3B99 /* AccountCreationTokenOp.m */ = {isa 
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; 
path = AccountCreationTokenOp.m; sourceTree = "<group>"; };
-               04CF1CB5187C8F4400E9516F /* Languages */ = {isa = 
PBXFileReference; lastKnownFileType = folder; path = Languages; sourceTree = 
"<group>"; };
                04D122301899B8AC006B9A30 /* AlertWebView.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
AlertWebView.h; sourceTree = "<group>"; };
                04D122311899B8AC006B9A30 /* AlertWebView.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= AlertWebView.m; sourceTree = "<group>"; };
                04D149D918877343006B4104 /* AlertLabel.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
AlertLabel.h; sourceTree = "<group>"; };
@@ -708,6 +719,26 @@
                        path = TopActionSheet;
                        sourceTree = "<group>";
                };
+               044213CD191D6F43006C03BF /* BundledPaths */ = {
+                       isa = PBXGroup;
+                       children = (
+                               044213D6191D7FFC006C03BF /* BundledPathsEnum.h 
*/,
+                               044213CE191D6F43006C03BF /* BundledPaths.h */,
+                               044213CF191D6F43006C03BF /* BundledPaths.m */,
+                       );
+                       path = BundledPaths;
+                       sourceTree = "<group>";
+               };
+               044213D1191D70BC006C03BF /* BundledJson */ = {
+                       isa = PBXGroup;
+                       children = (
+                               044213D5191D7FEA006C03BF /* BundledJsonEnum.h 
*/,
+                               044213D2191D70E9006C03BF /* BundledJson.h */,
+                               044213D3191D70E9006C03BF /* BundledJson.m */,
+                       );
+                       path = BundledJson;
+                       sourceTree = "<group>";
+               };
                0442F57C1900718600F55DF9 /* Fonts */ = {
                        isa = PBXGroup;
                        children = (
@@ -999,6 +1030,8 @@
                04D149E5188889CA006B4104 /* Operations */ = {
                        isa = PBXGroup;
                        children = (
+                               044213C6191C3C2A006C03BF /* ConfigFileSyncOp.h 
*/,
+                               044213C7191C3C2A006C03BF /* ConfigFileSyncOp.m 
*/,
                                04F27B7918FE19B700EDD838 /* PageHistoryOp.h */,
                                04F27B7A18FE19B700EDD838 /* PageHistoryOp.m */,
                                0406CEF418F8C390007EE43E /* LogEventOp.h */,
@@ -1197,14 +1230,16 @@
                                D469889318B52DA200DBE014 /* Main_iPhone.strings 
*/,
                                D46CD8C218A1AC4F0042959E /* Localizable.strings 
*/,
                                045A9F0C18F6090E0057EA85 /* assets */,
+                               044213D1191D70BC006C03BF /* BundledJson */,
+                               044213CD191D6F43006C03BF /* BundledPaths */,
                                04C43AB7183442FC006C643B /* Categories */,
                                040E5C50184673F2007AFE6F /* Data */,
                                04292FFB185FC026002A13FC /* Defines */,
                                0442F57C1900718600F55DF9 /* Fonts */,
                                04D34DA31863D2D600610A87 /* HTML Parsing */,
                                0466F44C183A30CC00EA1FD7 /* Images */,
+                               044213D7191D99FB006C03BF /* Json */,
                                0463639518A844380049EE4F /* Keychain */,
-                               04CF1CB5187C8F4400E9516F /* Languages */,
                                048A26741906268100395F53 /* PaddedLabel */,
                                04292FFD185FC2C7002A13FC /* Queues */,
                                0447866C1852B5010050563B /* Session */,
@@ -1398,7 +1433,6 @@
                        buildActionMask = 2147483647;
                        files = (
                                D469889518B52DA200DBE014 /* Main_iPhone.strings 
in Resources */,
-                               04CF1CB6187C8F4400E9516F /* Languages in 
Resources */,
                                0466F44F183A30CC00EA1FD7 /* 
logo-search-placeholder.png in Resources */,
                                D46CD8C418A1AC4F0042959E /* InfoPlist.strings 
in Resources */,
                                D4991454181D51DE00E6073C /* Images.xcassets in 
Resources */,
@@ -1418,6 +1452,7 @@
                                04B91AAE18E4056D00FFAA1C /* MainMenuRowView.xib 
in Resources */,
                                04090A33187F53E400577EDF /* clear.png in 
Resources */,
                                0412362E189C29EA00E0CF8E /* 
abuse-filter-disallowed.png in Resources */,
+                               044213D8191D99FB006C03BF /* Json in Resources 
*/,
                                0442F57E190071A100F55DF9 /* WikiFont.ttf in 
Resources */,
                                04082B5518ADA25A00FAF3D6 /* 
text_field_x_circle_g...@2x.png in Resources */,
                                0466F450183A30CC00EA1FD7 /* 
logo-search-placehol...@2x.png in Resources */,
@@ -1457,6 +1492,7 @@
                                044BD6B618849AD000FFE4BE /* 
SectionEditorViewController.m in Sources */,
                                04D34DAB1863D2D600610A87 /* TFHpple.m in 
Sources */,
                                0429301018604898002A13FC /* 
SavedPagesViewController.m in Sources */,
+                               044213D4191D70E9006C03BF /* BundledJson.m in 
Sources */,
                                04D149DF18877343006B4104 /* 
UIViewController+Alert.m in Sources */,
                                04FD6C7A184EBFCD002CA02F /* 
ArticleData.xcdatamodeld in Sources */,
                                048A26771906268100395F53 /* PaddedLabel.m in 
Sources */,
@@ -1492,6 +1528,7 @@
                                040E533C1885FB4E00AFBFE9 /* ImageData.m in 
Sources */,
                                04DB0BE618BC2E1E00B4BCF3 /* CaptchaResetOp.m in 
Sources */,
                                0415581C18ADFA5D00B81A59 /* UIImage+ColorMask.m 
in Sources */,
+                               044213C8191C3C2A006C03BF /* ConfigFileSyncOp.m 
in Sources */,
                                04A70FD7185BB6C300E24515 /* URLCache.m in 
Sources */,
                                04992BC018B687AF00A6C22B /* SearchOp.m in 
Sources */,
                                041A3B5E18E11ED90079FF1C /* LanguagesCell.m in 
Sources */,
@@ -1552,6 +1589,7 @@
                                0442F57B19006DCC00F55DF9 /* PageHistoryLabel.m 
in Sources */,
                                0447862F185145090050563B /* HistoryResultCell.m 
in Sources */,
                                04B0EA45190AFDD8007458AF /* ArticleImporter.m 
in Sources */,
+                               044213D0191D6F43006C03BF /* BundledPaths.m in 
Sources */,
                                0406CEF618F8C390007EE43E /* LogEventOp.m in 
Sources */,
                                0476967C18BBFC9400071963 /* AccountCreationOp.m 
in Sources */,
                        );
diff --git a/wikipedia/AppDelegate.m b/wikipedia/AppDelegate.m
index fe9868d..4f372f7 100644
--- a/wikipedia/AppDelegate.m
+++ b/wikipedia/AppDelegate.m
@@ -20,6 +20,12 @@
     [self registerStandardUserDefaults];
     [self systemWideStyleOverrides];
 
+    // We may need to overwrite these json files form the server, so they need 
to be
+    // placed in the "AppData/Documents/" folder and access and update them 
there.
+    // This method copies bundled "Json" folder over to "AppData/Documents/" 
if it's
+    // not already there.
+    [self copyBundledFolderToAppDataDocuments:@"Json"];
+
     // Enables Alignment Rect highlighting for debugging
     //[[NSUserDefaults standardUserDefaults] setBool:NO 
forKey:@"UIViewShowAlignmentRects"];
     //[[NSUserDefaults standardUserDefaults] synchronize];
@@ -29,6 +35,23 @@
     return YES;
 }
 
+-(void)copyBundledFolderToAppDataDocuments:(NSString *)folderName
+{
+
+//TODO: modify so if new app release has new bundled files or folders these
+// get copied too. Presently if folderName is seen to already exist in the
+// AppData/Documents/ directory no copying occurs.
+
+    NSFileManager *fileManager = [[NSFileManager defaultManager] init];
+    NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, 
NSUserDomainMask, YES);
+    NSString *documentsPath = [[paths objectAtIndex:0] 
stringByAppendingPathComponent:folderName];
+    if (![fileManager fileExistsAtPath:documentsPath]){
+        NSString *bundledPath = [[[NSBundle mainBundle] resourcePath] 
stringByAppendingPathComponent:folderName];
+        //[fileManager createDirectoryAtPath: documentDBFolderPath 
attributes:nil];
+        [fileManager copyItemAtPath:bundledPath toPath:documentsPath 
error:nil];
+    }
+}
+
 -(void)registerStandardUserDefaults
 {
     // Register default default values.
diff --git a/wikipedia/BundledJson/BundledJson.h 
b/wikipedia/BundledJson/BundledJson.h
new file mode 100644
index 0000000..bcf2bc1
--- /dev/null
+++ b/wikipedia/BundledJson/BundledJson.h
@@ -0,0 +1,15 @@
+//  Created by Monte Hurd on 5/9/14.
+//  Copyright (c) 2013 Wikimedia Foundation. Provided under MIT-style license; 
please copy and modify!
+
+#import "BundledPathsEnum.h"
+#import "BundledJsonEnum.h"
+
+@interface BundledJson : NSObject
+
++ (NSDictionary *)dictionaryFromBundledJsonFile:(BundledJsonFile)file;
+
++ (NSArray *)arrayFromBundledJsonFile:(BundledJsonFile)file;
+
++ (BOOL)isRefreshNeededForBundledJsonFile:(BundledJsonFile)file 
maxAge:(CGFloat)maxAge;
+
+@end
diff --git a/wikipedia/BundledJson/BundledJson.m 
b/wikipedia/BundledJson/BundledJson.m
new file mode 100644
index 0000000..7a58086
--- /dev/null
+++ b/wikipedia/BundledJson/BundledJson.m
@@ -0,0 +1,58 @@
+//  Created by Monte Hurd on 5/9/14.
+//  Copyright (c) 2013 Wikimedia Foundation. Provided under MIT-style license; 
please copy and modify!
+
+#import "BundledJson.h"
+#import "BundledPaths.h"
+
+@implementation BundledJson
+
++ (NSDictionary *)dictionaryFromBundledJsonFile:(BundledJsonFile)file
+{
+    NSError *error = nil;
+    NSData *fileData = [NSData dataWithContentsOfFile:[BundledPaths 
bundledJsonFilePath:file] options:0 error:&error];
+    if (error) return @{};
+    error = nil;
+    NSMutableDictionary *result = [NSJSONSerialization 
JSONObjectWithData:fileData options:0 error:&error];
+    return (error) ? @{}: result;
+}
+
++ (NSArray *)arrayFromBundledJsonFile:(BundledJsonFile)file
+{
+    NSError *error = nil;
+    NSData *fileData = [NSData dataWithContentsOfFile:[BundledPaths 
bundledJsonFilePath:file] options:0 error:&error];
+    if (error) return @[];
+    error = nil;
+    NSArray *result = [NSJSONSerialization JSONObjectWithData:fileData 
options:0 error:&error];
+    return (error) ? @[]: result;
+}
+
+// Returns YES if the local version of the config file doesn't exist or is 
older than maxAge.
++ (BOOL)isRefreshNeededForBundledJsonFile:(BundledJsonFile)file 
maxAge:(CGFloat)maxAge
+{
+    NSString *path = [BundledPaths bundledJsonFilePath:file];
+    
+    BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path 
isDirectory:NO];
+    if (!fileExists){
+        NSLog(@"REFRESH NEEDED");
+        return YES;
+    }
+    
+    NSDate *lastModified = nil;
+    NSError *error = nil;
+    NSURL *url = [NSURL fileURLWithPath:path];
+    [url getResourceValue: &lastModified
+                   forKey: NSURLContentModificationDateKey
+                    error: &error];
+    if (!error){
+        NSTimeInterval currentAge = [[NSDate date] 
timeIntervalSinceDate:lastModified];
+        NSLog(@"currentAge = %f maxAge = %f", currentAge, maxAge);
+        if (currentAge > maxAge){
+            NSLog(@"REFRESH NEEDED");
+            return YES;
+        }
+    }
+    NSLog(@"NO REFRESH NEEDED");
+    return NO;
+}
+
+@end
diff --git a/wikipedia/BundledJson/BundledJsonEnum.h 
b/wikipedia/BundledJson/BundledJsonEnum.h
new file mode 100644
index 0000000..e301b98
--- /dev/null
+++ b/wikipedia/BundledJson/BundledJsonEnum.h
@@ -0,0 +1,7 @@
+
+typedef NS_ENUM(NSUInteger, BundledJsonFile) {
+    BUNDLED_JSON_UNDEFINED = 0,
+    BUNDLED_JSON_CONFIG = 1,
+    BUNDLED_JSON_LANGUAGES = 2,
+    BUNDLED_JSON_MAINPAGES = 3
+};
diff --git a/wikipedia/BundledPaths/BundledPaths.h 
b/wikipedia/BundledPaths/BundledPaths.h
new file mode 100644
index 0000000..7601df1
--- /dev/null
+++ b/wikipedia/BundledPaths/BundledPaths.h
@@ -0,0 +1,12 @@
+//  Created by Monte Hurd on 5/9/14.
+//  Copyright (c) 2013 Wikimedia Foundation. Provided under MIT-style license; 
please copy and modify!
+
+#import "BundledPathsEnum.h"
+#import "BundledJsonEnum.h"
+
+@interface BundledPaths : NSObject
+
++ (NSString *)bundledJsonFilePath:(BundledJsonFile)file;
++ (NSURL *)bundledJsonFileRemoteUrl:(BundledJsonFile)file;
+
+@end
diff --git a/wikipedia/BundledPaths/BundledPaths.m 
b/wikipedia/BundledPaths/BundledPaths.m
new file mode 100644
index 0000000..d115f70
--- /dev/null
+++ b/wikipedia/BundledPaths/BundledPaths.m
@@ -0,0 +1,96 @@
+//  Created by Monte Hurd on 5/9/14.
+//  Copyright (c) 2013 Wikimedia Foundation. Provided under MIT-style license; 
please copy and modify!
+
+#import "BundledPaths.h"
+#import "BundledJson.h"
+
+@implementation BundledPaths
+
++ (NSString *)nameForFile:(BundledJsonFile)bundledJsonFile
+{
+    switch (bundledJsonFile) {
+        case BUNDLED_JSON_CONFIG:
+            return @"ios.json";
+            break;
+        case BUNDLED_JSON_LANGUAGES:
+            return @"languages.json";
+            break;
+        case BUNDLED_JSON_MAINPAGES:
+            return @"mainpages.json";
+            break;
+        default:
+            return BUNDLED_JSON_UNDEFINED;
+            break;
+    }
+}
+
++ (NSString *)nameForPath:(BundledPath)bundledPath
+{
+    switch (bundledPath) {
+        case BUNDLED_PATH_CONFIG:
+            return @"config";
+            break;
+        case BUNDLED_PATH_LANGUAGES:
+        case BUNDLED_PATH_MAINPAGES:
+            return @"Languages";
+            break;
+        default:
+            return BUNDLED_PATH_UNDEFINED;
+            break;
+    }
+}
+
++ (BundledPath)bundledPathForFile:(BundledJsonFile)file
+{
+    switch (file) {
+        case BUNDLED_JSON_CONFIG:
+            return BUNDLED_PATH_CONFIG;
+            break;
+        case BUNDLED_JSON_LANGUAGES:
+            return BUNDLED_PATH_LANGUAGES;
+            break;
+        case BUNDLED_JSON_MAINPAGES:
+            return BUNDLED_PATH_MAINPAGES;
+            break;
+        default:
+            return BUNDLED_PATH_UNDEFINED;
+            break;
+    }
+}
+
++ (NSString *)remoteUrlForFile:(BundledJsonFile)bundledJsonFile
+{
+    // For now only the config file is remote synced.
+    switch (bundledJsonFile) {
+        case BUNDLED_JSON_CONFIG:
+            return 
@"https://bits.wikimedia.org/static-current/extensions/MobileApp/config/ios.json";;
+            break;
+        default:
+            return @"";
+            break;
+    }
+}
+
++(NSString *)pathForFolder:(BundledPath)folder file:(BundledJsonFile)file
+{
+    NSString *folderName = [self nameForPath:folder];
+    NSString *fileName = [self nameForFile:file];
+    NSArray *documentsPath = NSSearchPathForDirectoriesInDomains( 
NSDocumentDirectory, NSUserDomainMask, YES);
+    NSString *jsonPath = [[documentsPath objectAtIndex:0] 
stringByAppendingPathComponent:@"Json"];
+    NSString *folderPath = [jsonPath 
stringByAppendingPathComponent:folderName];
+    NSString *filePath = [folderPath stringByAppendingPathComponent:fileName];
+    return filePath;
+}
+
++ (NSString *)bundledJsonFilePath:(BundledJsonFile)file
+{
+    BundledPath bundledPath = [self bundledPathForFile:file];
+    return [self pathForFolder:bundledPath file:file];
+}
+
++ (NSURL *)bundledJsonFileRemoteUrl:(BundledJsonFile)file
+{
+    return [NSURL URLWithString:[self remoteUrlForFile:file]];
+}
+
+@end
diff --git a/wikipedia/BundledPaths/BundledPathsEnum.h 
b/wikipedia/BundledPaths/BundledPathsEnum.h
new file mode 100644
index 0000000..189a518
--- /dev/null
+++ b/wikipedia/BundledPaths/BundledPathsEnum.h
@@ -0,0 +1,7 @@
+
+typedef NS_ENUM(NSUInteger, BundledPath) {
+    BUNDLED_PATH_UNDEFINED = 0,
+    BUNDLED_PATH_CONFIG = 1,
+    BUNDLED_PATH_LANGUAGES = 2,
+    BUNDLED_PATH_MAINPAGES = 3
+};
diff --git a/wikipedia/Data/Operations/ConfigFileSyncOp.h 
b/wikipedia/Data/Operations/ConfigFileSyncOp.h
new file mode 100644
index 0000000..a27fc90
--- /dev/null
+++ b/wikipedia/Data/Operations/ConfigFileSyncOp.h
@@ -0,0 +1,22 @@
+//  Created by Monte Hurd on 5/8/14.
+//  Copyright (c) 2013 Wikimedia Foundation. Provided under MIT-style license; 
please copy and modify!
+
+#import "MWNetworkOp.h"
+
+#import "BundledJsonEnum.h"
+
+@interface ConfigFileSyncOp : MWNetworkOp
+
+// Syncs any bundled app json file with a remote file.
+
+// Only does so if age of app file exceeds maxAge or if the file isn't found 
in app.
+
+// Nice because we can sync any bundled files with any periodicity
+// required just by firing these operations off occasionally.
+
+// They self-cancel if maxAge has not been exceeded, so fire away.
+
+- (id)initForBundledJsonFile: (BundledJsonFile)file
+                      maxAge: (CGFloat)maxAge;
+
+@end
diff --git a/wikipedia/Data/Operations/ConfigFileSyncOp.m 
b/wikipedia/Data/Operations/ConfigFileSyncOp.m
new file mode 100644
index 0000000..87fc1c2
--- /dev/null
+++ b/wikipedia/Data/Operations/ConfigFileSyncOp.m
@@ -0,0 +1,63 @@
+//  Created by Monte Hurd on 5/8/14.
+//  Copyright (c) 2013 Wikimedia Foundation. Provided under MIT-style license; 
please copy and modify!
+
+#import "ConfigFileSyncOp.h"
+#import "MWNetworkActivityIndicatorManager.h"
+#import "NSURLRequest+DictionaryRequest.h"
+
+#import "BundledJson.h"
+#import "BundledPaths.h"
+
+@implementation ConfigFileSyncOp
+
+- (id)initForBundledJsonFile: (BundledJsonFile)file
+                      maxAge: (CGFloat)maxAge
+{
+    self = [super init];
+    if (self) {
+
+        self.request = [NSURLRequest getRequestWithURL: [BundledPaths 
bundledJsonFileRemoteUrl:file]
+                                            parameters: nil];
+        
+        __weak ConfigFileSyncOp *weakSelf = self;
+        self.aboutToStart = ^{
+            
+            // Cancel the operation if the existing file hasn't aged enough.
+            BOOL shouldRefresh = [BundledJson 
isRefreshNeededForBundledJsonFile:file maxAge:maxAge];
+
+            if (!shouldRefresh) {
+                [weakSelf cancel];
+                return;
+            }
+
+            [[MWNetworkActivityIndicatorManager sharedManager] push];
+        };
+        self.completionBlock = ^(){
+            [[MWNetworkActivityIndicatorManager sharedManager] pop];
+            
+            if(weakSelf.isCancelled){
+                return;
+            }
+            
+            if (weakSelf.error) {
+                return;
+            }
+
+            // If it got this far, then a refresh was needed and has completed.
+            if (weakSelf.dataRetrieved) {
+                NSString *jsonString = [[NSString alloc] 
initWithData:weakSelf.dataRetrieved encoding:NSUTF8StringEncoding];
+                //NSLog(@"jsonString = %@", jsonString);
+                if (!jsonString) return;
+                NSString *filePath = [BundledPaths bundledJsonFilePath:file];
+                NSError *error = nil;
+                [jsonString writeToFile:filePath atomically:YES 
encoding:NSUTF8StringEncoding error:&error];
+                if (error) {
+                    NSLog(@"%@", error.localizedDescription);
+                }
+            }
+        };
+    }
+    return self;
+}
+
+@end
diff --git a/wikipedia/Data/Operations/DownloadLangLinksOp.h 
b/wikipedia/Data/Operations/DownloadLangLinksOp.h
index 7c60b9b..68fbe47 100644
--- a/wikipedia/Data/Operations/DownloadLangLinksOp.h
+++ b/wikipedia/Data/Operations/DownloadLangLinksOp.h
@@ -7,7 +7,7 @@
 
 - (id)initForPageTitle: (NSString *)title
                 domain: (NSString *)domain
-          allLanguages: (NSMutableArray *)allLanguages
+          allLanguages: (NSArray *)allLanguages
        completionBlock: (void (^)(NSArray *))completionBlock
         cancelledBlock: (void (^)(NSError *))cancelledBlock
             errorBlock: (void (^)(NSError *))errorBlock
diff --git a/wikipedia/Data/Operations/DownloadLangLinksOp.m 
b/wikipedia/Data/Operations/DownloadLangLinksOp.m
index 0d44e4b..e290721 100644
--- a/wikipedia/Data/Operations/DownloadLangLinksOp.m
+++ b/wikipedia/Data/Operations/DownloadLangLinksOp.m
@@ -10,7 +10,7 @@
 
 - (id)initForPageTitle: (NSString *)title
                 domain: (NSString *)domain
-          allLanguages: (NSMutableArray *)allLanguages
+          allLanguages: (NSArray *)allLanguages
        completionBlock: (void (^)(NSArray *))completionBlock
         cancelledBlock: (void (^)(NSError *))cancelledBlock
             errorBlock: (void (^)(NSError *))errorBlock
diff --git a/wikipedia/Languages/languages.json 
b/wikipedia/Json/Languages/languages.json
similarity index 100%
rename from wikipedia/Languages/languages.json
rename to wikipedia/Json/Languages/languages.json
diff --git a/wikipedia/Languages/mainpages.json 
b/wikipedia/Json/Languages/mainpages.json
similarity index 100%
rename from wikipedia/Languages/mainpages.json
rename to wikipedia/Json/Languages/mainpages.json
diff --git a/wikipedia/Json/config/ios.json b/wikipedia/Json/config/ios.json
new file mode 100644
index 0000000..75b5c8d
--- /dev/null
+++ b/wikipedia/Json/config/ios.json
@@ -0,0 +1,3 @@
+{
+    "disableAnonEditing": false
+}
diff --git a/wikipedia/Queues/QueuesSingleton.h 
b/wikipedia/Queues/QueuesSingleton.h
index 1607054..9618d2c 100644
--- a/wikipedia/Queues/QueuesSingleton.h
+++ b/wikipedia/Queues/QueuesSingleton.h
@@ -22,6 +22,8 @@
 @property (strong, nonatomic) NSOperationQueue *eventLoggingQ;
 @property (strong, nonatomic) NSOperationQueue *pageHistoryQ;
 
+@property (strong, nonatomic) NSOperationQueue *bundledFileSyncQ;
+
 + (QueuesSingleton *)sharedInstance;
 
 @end
diff --git a/wikipedia/Queues/QueuesSingleton.m 
b/wikipedia/Queues/QueuesSingleton.m
index 6289a5a..bbf87c2 100644
--- a/wikipedia/Queues/QueuesSingleton.m
+++ b/wikipedia/Queues/QueuesSingleton.m
@@ -32,6 +32,7 @@
         self.randomArticleQ = [[NSOperationQueue alloc] init];
         self.eventLoggingQ = [[NSOperationQueue alloc] init];
         self.pageHistoryQ = [[NSOperationQueue alloc] init];
+        self.bundledFileSyncQ = [[NSOperationQueue alloc] init];
         //[self setupQMonitorLogging];
     }
     return self;
diff --git a/wikipedia/Session/SessionSingleton.h 
b/wikipedia/Session/SessionSingleton.h
index 55c03be..33e7897 100644
--- a/wikipedia/Session/SessionSingleton.h
+++ b/wikipedia/Session/SessionSingleton.h
@@ -28,7 +28,6 @@
 @property (strong, atomic) NSArray *unsupportedCharactersLanguageIds;
 
 -(NSURL *)urlForDomain:(NSString *)domain;
--(NSMutableArray *)getBundledLanguagesJson;
 -(NSString *)domainNameForCode:(NSString *)code;
 
 -(NSString *)mainArticleTitleForCode:(NSString *)code;
diff --git a/wikipedia/Session/SessionSingleton.m 
b/wikipedia/Session/SessionSingleton.m
index d10fe95..545791e 100644
--- a/wikipedia/Session/SessionSingleton.m
+++ b/wikipedia/Session/SessionSingleton.m
@@ -2,6 +2,8 @@
 //  Copyright (c) 2013 Wikimedia Foundation. Provided under MIT-style license; 
please copy and modify!
 
 #import "SessionSingleton.h"
+#import "BundledPaths.h"
+#import "BundledJson.h"
 
 @implementation SessionSingleton
 
@@ -117,12 +119,8 @@
 
 -(NSString *)domainNameForCode:(NSString *)code
 {
-    NSError *error = nil;
-    NSData *fileData = [NSData dataWithContentsOfFile:[self 
bundledLanguagesJsonPath] options:0 error:&error];
-    if (error) return nil;
-    error = nil;
-    NSArray *result = [NSJSONSerialization JSONObjectWithData:fileData 
options:0 error:&error];
-    if (!error) {
+    NSArray *result = [BundledJson 
arrayFromBundledJsonFile:BUNDLED_JSON_LANGUAGES];
+    if (result.count > 0) {
         for (NSDictionary *d in result) {
             if ([d[@"code"] isEqualToString:code]) {
                 return d[@"name"];
@@ -134,39 +132,9 @@
     }
 }
 
-- (NSString *)bundledLanguagesJsonPath
-{
-    return [[[NSBundle mainBundle] bundlePath] 
stringByAppendingString:@"/Languages/languages.json"];
-}
-
--(NSMutableArray *)getBundledLanguagesJson
-{
-    NSError *error = nil;
-    NSData *fileData = [NSData dataWithContentsOfFile:[[SessionSingleton 
sharedInstance] bundledLanguagesJsonPath] options:0 error:&error];
-    if (error) return [@[] mutableCopy];
-    error = nil;
-    NSArray *result = [NSJSONSerialization JSONObjectWithData:fileData 
options:0 error:&error];
-    return (error) ? [@[] mutableCopy]: [result mutableCopy];
-}
-
-- (NSString *)bundledMainArticleTitlesJsonPath
-{
-    return [[[NSBundle mainBundle] bundlePath] 
stringByAppendingString:@"/Languages/mainpages.json"];
-}
-
--(NSMutableDictionary *)getBundledMainArticleTitlesJson
-{
-    NSError *error = nil;
-    NSData *fileData = [NSData dataWithContentsOfFile:[[SessionSingleton 
sharedInstance] bundledMainArticleTitlesJsonPath] options:0 error:&error];
-    if (error) return @{}.mutableCopy;
-    error = nil;
-    NSDictionary *result = [NSJSONSerialization JSONObjectWithData:fileData 
options:0 error:&error];
-    return (error) ? @{}.mutableCopy: [result mutableCopy];
-}
-
 -(NSString *)mainArticleTitleForCode:(NSString *)code
 {
-    NSMutableDictionary *mainPageNames = [self 
getBundledMainArticleTitlesJson];
+    NSDictionary *mainPageNames = [BundledJson 
dictionaryFromBundledJsonFile:BUNDLED_JSON_MAINPAGES];
     return mainPageNames[code];
 }
 
diff --git a/wikipedia/View Controllers/Languages/LanguagesTableVC.m 
b/wikipedia/View Controllers/Languages/LanguagesTableVC.m
index 1f31fa4..a6cadea 100644
--- a/wikipedia/View Controllers/Languages/LanguagesTableVC.m
+++ b/wikipedia/View Controllers/Languages/LanguagesTableVC.m
@@ -10,6 +10,7 @@
 #import "LanguagesSectionHeadingLabel.h"
 #import "NavController.h"
 #import "Defines.h"
+#import "BundledJson.h"
 
 #import "UIViewController+Alert.h"
 
@@ -70,7 +71,7 @@
     if(self.downloadLanguagesForCurrentArticle){
         [self downloadLangLinkData];
     }else{
-        self.languagesData = [[SessionSingleton sharedInstance] 
getBundledLanguagesJson];
+        self.languagesData = [BundledJson 
arrayFromBundledJsonFile:BUNDLED_JSON_LANGUAGES];
             self.headerView.hidden = NO;
         [self reloadTableDataFiltered];
     }
@@ -292,7 +293,7 @@
     DownloadLangLinksOp *langLinksOp =
     [[DownloadLangLinksOp alloc] initForPageTitle: [SessionSingleton 
sharedInstance].currentArticleTitle
                                            domain: [SessionSingleton 
sharedInstance].currentArticleDomain
-                                     allLanguages: [[SessionSingleton 
sharedInstance] getBundledLanguagesJson]
+                                     allLanguages: [BundledJson 
arrayFromBundledJsonFile:BUNDLED_JSON_LANGUAGES]
                                   completionBlock: ^(NSArray *result){
                                       
                                       [[NSOperationQueue mainQueue] 
addOperationWithBlock: ^ {
diff --git a/wikipedia/View Controllers/Preview/PreviewChoicesMenuView.m 
b/wikipedia/View Controllers/Preview/PreviewChoicesMenuView.m
index ae0b7f9..770ffbe 100644
--- a/wikipedia/View Controllers/Preview/PreviewChoicesMenuView.m
+++ b/wikipedia/View Controllers/Preview/PreviewChoicesMenuView.m
@@ -5,6 +5,7 @@
 #import "PaddedLabel.h"
 #import "WikipediaAppUtils.h"
 #import "NSString+FormattedAttributedString.h"
+#import "BundledJson.h"
 
 #define PREVIEW_BLUE_COLOR [UIColor colorWithRed:0.13 green:0.42 blue:0.68 
alpha:1.0]
 
@@ -14,6 +15,8 @@
 @interface PreviewChoicesMenuView(){
 
 }
+
+@property (weak, nonatomic) IBOutlet UIView *topRowContainer;
 
 @end
 
@@ -52,7 +55,6 @@
     self.signInView.backgroundColor = PREVIEW_BLUE_COLOR;
     
     [self underlineLabelText: self.signInTitleLabel];
-    [self underlineLabelText: self.saveAnonTitleLabel];
 
     /*
     self.signInTitleLabel.text = [@" abc " randomlyRepeatMaxTimes:100];
@@ -61,6 +63,21 @@
     self.saveAnonSubTitleLabel.text = [@" abc " randomlyRepeatMaxTimes:100];
     [self randomlyColorSubviews];
     */
+
+    if ([self isAnonymousEditingDisabled]) {
+        [self.saveAnonView removeFromSuperview];
+        // The right side of the signInView had been constrained to the left 
side of the
+        // saveAnonView, but now that the saveAnonView is gone the right side 
of the
+        // signInView needs to be constrained to the right side of the 
topRowContainer.
+        [self.topRowContainer addConstraints:
+         [NSLayoutConstraint constraintsWithVisualFormat: @"H:[signInView]|"
+                                                 options: 0
+                                                 metrics: nil
+                                                   views: @{@"signInView" : 
self.signInView}]
+         ];
+    }else{
+        [self underlineLabelText: self.saveAnonTitleLabel];
+    }
 }
 
 -(void)underlineLabelText:(UILabel *)label
@@ -79,4 +96,14 @@
     label.attributedText = aString;
 }
 
+-(BOOL)isAnonymousEditingDisabled
+{
+    NSDictionary *iosConfigJson = [BundledJson 
dictionaryFromBundledJsonFile:BUNDLED_JSON_CONFIG];
+    NSNumber *disableAnonEditing = iosConfigJson[@"disableAnonEditing"];
+    if (disableAnonEditing && (disableAnonEditing.boolValue == YES)) {
+        return YES;
+    }
+    return NO;
+}
+
 @end
diff --git a/wikipedia/View Controllers/Preview/PreviewChoicesMenuView.xib 
b/wikipedia/View Controllers/Preview/PreviewChoicesMenuView.xib
index 2ad687f..57d3cd9 100644
--- a/wikipedia/View Controllers/Preview/PreviewChoicesMenuView.xib
+++ b/wikipedia/View Controllers/Preview/PreviewChoicesMenuView.xib
@@ -102,6 +102,7 @@
                 <outlet property="signInSubTitleLabel" 
destination="dVR-8O-7eG" id="hus-QR-GXZ"/>
                 <outlet property="signInTitleLabel" destination="PPh-Eu-usA" 
id="LO3-x5-pCZ"/>
                 <outlet property="signInView" destination="JYT-4n-1ej" 
id="k1q-zU-i4A"/>
+                <outlet property="topRowContainer" destination="2HE-pz-gbX" 
id="8XN-KB-zcG"/>
             </connections>
         </view>
     </objects>
diff --git a/wikipedia/View Controllers/TopNav/NavController.m b/wikipedia/View 
Controllers/TopNav/NavController.m
index f6b3b0b..4eaa78f 100644
--- a/wikipedia/View Controllers/TopNav/NavController.m
+++ b/wikipedia/View Controllers/TopNav/NavController.m
@@ -473,7 +473,7 @@
         case NAVBAR_MODE_EDIT_WIKITEXT_LOGIN_OR_SAVE_ANONYMOUSLY:
             self.label.text = @"";
             self.navBarSubViewsHorizontalVFLString =
-                
@"H:|[NAVBAR_BUTTON_PENCIL(50)][NAVBAR_VERTICAL_LINE_1(singlePixel)]-(10)-[NAVBAR_LABEL]-(10)-[NAVBAR_VERTICAL_LINE_2(singlePixel)][NAVBAR_BUTTON_CHECK(50)]|";
+                
@"H:|[NAVBAR_BUTTON_PENCIL(50)][NAVBAR_VERTICAL_LINE_1(singlePixel)]-(10)-[NAVBAR_LABEL]|";
             break;
         case NAVBAR_MODE_EDIT_WIKITEXT_SAVE:
             self.label.text = 
MWLocalizedString(@"navbar-title-mode-edit-wikitext-save", nil);
diff --git a/wikipedia/View Controllers/WebView/WebViewController.m 
b/wikipedia/View Controllers/WebView/WebViewController.m
index 4e299a7..86598ba 100644
--- a/wikipedia/View Controllers/WebView/WebViewController.m
+++ b/wikipedia/View Controllers/WebView/WebViewController.m
@@ -39,6 +39,8 @@
 #import "DataMigrator.h"
 #import "ArticleImporter.h"
 
+#import "ConfigFileSyncOp.h"
+
 #define TOC_TOGGLE_ANIMATION_DURATION 0.3f
 #define NAV ((NavController *)self.navigationController)
 
@@ -174,7 +176,12 @@
 -(void)viewDidAppear:(BOOL)animated
 {
     [super viewDidAppear:animated];
-    
+
+    // Don't move this to viewDidLoad - this is because viewDidLoad may only 
get
+    // called very occasionally as app suspend/resume probably doesn't cause
+    // viewDidLoad to fire.
+    [self sycnConfigIosJsonIfNecessary];
+
     //[self.view randomlyColorSubviews];
 }
 
@@ -191,6 +198,21 @@
     [super viewWillDisappear:animated];
 }
 
+#pragma mark Sync bundled config/ios.json if necessary
+
+-(void)sycnConfigIosJsonIfNecessary
+{
+    // Sync config/ios.json at most once per day.
+    CGFloat maxAge = 60 * 60 * 24;
+
+    ConfigFileSyncOp *configSyncOp =
+    [[ConfigFileSyncOp alloc] initForBundledJsonFile: BUNDLED_JSON_CONFIG
+                                              maxAge: maxAge];
+    
+    [[QueuesSingleton sharedInstance].bundledFileSyncQ cancelAllOperations];
+    [[QueuesSingleton sharedInstance].bundledFileSyncQ 
addOperation:configSyncOp];
+}
+
 #pragma mark Edit section
 
 -(void)showSectionEditor

-- 
To view, visit https://gerrit.wikimedia.org/r/132587
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ia64c060806c14cbf08cacb3168af8f471c8de048
Gerrit-PatchSet: 2
Gerrit-Project: apps/ios/wikipedia
Gerrit-Branch: master
Gerrit-Owner: Mhurd <mh...@wikimedia.org>
Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org>
Gerrit-Reviewer: Dr0ptp4kt <ab...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to