diff --git a/README.md b/README.md index e20e442123b5..5a4b21e02fe5 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,34 @@ WordPress for iOS -Resources ---------------------------------------------------------------- -Developer blog: +## Resources + +### Developer blog + http://dev.ios.wordpress.org/ -Source code: -http://ios.trac.wordpress.org/ -http://ios.svn.wordpress.org/ +### Style guide + +https://github.com/wordpress-mobile/WordPress-iOS/wiki/WordPress-for-iOS-Style-Guide + +### To report an issue + +http://ios.trac.wordpress.org/newticket + +You'll need a WordPress.org account. If you don't have one you can +register here: + +http://wordpress.org/support/register.php + +### Source Code + +SVN: http://ios.svn.wordpress.org/ + +SVN browser: http://ios.trac.wordpress.org/browser + +Github mirror: https://github.com/wordpress-mobile/WordPress-iOS/ +## Building -Building ---------------------------------------------------------------- Starting with changeset 3633 version 3.2, WordPress for iOS uses Cocoapods (http://cocoapods.org/) to manage third party libraries. Trying to build the project by itself (WordPress.xcproj) after launching will result in an error, as the resources managed by cocoapods are not included. Instead, launch the workspace by either double clicking on WordPress.xcworkspace file, or launch Xcode and choose File > Open and browse to WordPress.xcworkspace. diff --git a/WordPress/Classes/AboutViewController.h b/WordPress/Classes/AboutViewController.h index 2ba00dab87da..1282cea3c051 100644 --- a/WordPress/Classes/AboutViewController.h +++ b/WordPress/Classes/AboutViewController.h @@ -9,13 +9,5 @@ #import #import "WordPressAppDelegate.h" -@interface AboutViewController : UIViewController - -@property (nonatomic, strong) IBOutlet UIView *logoView; -@property (nonatomic, strong) IBOutlet UIView *buttonsView; - -- (IBAction)viewTermsOfService:(id)sender; -- (IBAction)viewPrivacyPolicy:(id)sender; -- (IBAction)viewWebsite:(id)sender; - +@interface AboutViewController : UIViewController @end diff --git a/WordPress/Classes/AboutViewController.m b/WordPress/Classes/AboutViewController.m index a3049b954170..ca2678b2fa79 100644 --- a/WordPress/Classes/AboutViewController.m +++ b/WordPress/Classes/AboutViewController.m @@ -10,9 +10,11 @@ #import "ReachabilityUtils.h" #import "WPWebViewController.h" -@interface AboutViewController (Private) -- (void)dismiss; -- (void)openURLWithString:(NSString *)path; +@interface AboutViewController() + +@property (nonatomic, strong) IBOutlet UIView *logoView; +@property (nonatomic, strong) IBOutlet UIView *buttonsView; + @end @implementation AboutViewController @@ -20,6 +22,9 @@ @implementation AboutViewController @synthesize buttonsView; @synthesize logoView; +CGFloat const AboutViewLandscapeButtonsY = -20.0f; +CGFloat const AboutViewPortraitButtonsY = 90.0f; + // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { [FileLogger log:@"%@ %@", self, NSStringFromSelector(_cmd)]; @@ -28,7 +33,7 @@ - (void)viewDidLoad { self.navigationItem.title = NSLocalizedString(@"About", @"About this app (information page title)"); self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"welcome_bg_pattern.png"]]; - if( [self.navigationController.viewControllers count] == 1 ) + if([self.navigationController.viewControllers count] == 1) self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Close", @"") style:UIBarButtonItemStyleBordered target:self action:@selector(dismiss)]; } @@ -37,20 +42,17 @@ - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interface return [super shouldAutorotateToInterfaceOrientation:interfaceOrientation]; } - - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - if( IS_IPHONE ) { - if ( YES == UIInterfaceOrientationIsLandscape(toInterfaceOrientation) ) { + if (IS_IPHONE) { + CGRect frame = buttonsView.frame; + if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) { self.logoView.hidden = YES; - CGRect frame = buttonsView.frame; - frame.origin.y = -20.0f; - self.buttonsView.frame = frame; + frame.origin.y = AboutViewLandscapeButtonsY; } else { self.logoView.hidden = NO; - CGRect frame = buttonsView.frame; - frame.origin.y = 90.0f; - self.buttonsView.frame = frame; + frame.origin.y = AboutViewPortraitButtonsY; } + self.buttonsView.frame = frame; } } diff --git a/WordPress/Classes/AddSiteViewController.h b/WordPress/Classes/AddSiteViewController.h index b681fdd42827..58671b1b12a1 100644 --- a/WordPress/Classes/AddSiteViewController.h +++ b/WordPress/Classes/AddSiteViewController.h @@ -8,7 +8,5 @@ #import #import "EditSiteViewController.h" -@interface AddSiteViewController : EditSiteViewController { -} - +@interface AddSiteViewController : EditSiteViewController @end diff --git a/WordPress/Classes/AddSiteViewController.m b/WordPress/Classes/AddSiteViewController.m index ae842f4df91f..7d2a9ce2e9db 100644 --- a/WordPress/Classes/AddSiteViewController.m +++ b/WordPress/Classes/AddSiteViewController.m @@ -16,11 +16,13 @@ - (void)validationDidFail:(id)wrong; @implementation AddSiteViewController +CGSize const AddSiteLogoSize = { 320.0, 70.0 }; + - (void)viewDidLoad { [super viewDidLoad]; UIImageView *logoImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"logo_wporg"]]; - logoImage.frame = CGRectMake(0.0f, 0.0f, 320.0f, 70.0f); + logoImage.frame = CGRectMake(0.0f, 0.0f, AddSiteLogoSize.width, AddSiteLogoSize.height); logoImage.autoresizingMask = UIViewAutoresizingFlexibleWidth; logoImage.contentMode = UIViewContentModeCenter; tableView.tableHeaderView = logoImage; @@ -35,64 +37,26 @@ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInte return nil; } -- (void)validationSuccess:(NSString *)xmlrpc { - WordPressAppDelegate *appDelegate = [WordPressAppDelegate sharedWordPressApplicationDelegate]; - NSLog(@"hasSubsites: %@", subsites); +- (void)validationSuccess:(NSString *)xmlRpc { + WPFLog(@"hasSubsites: %@", subsites); if ([subsites count] > 0) { // If the user has entered the URL of a site they own on a MultiSite install, // assume they want to add that specific site. NSDictionary *subsite = nil; - if ([subsites count] > 1) - subsite = [[subsites filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"xmlrpc = %@", xmlrpc]] lastObject]; + if ([subsites count] > 1) { + subsite = [[subsites filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"xmlrpc = %@", xmlRpc]] lastObject]; + } + + if (subsite == nil) { + subsite = [subsites objectAtIndex:0]; + } if ([subsites count] > 1 && [[subsite objectForKey:@"blogid"] isEqualToString:@"1"]) { - AddUsersBlogsViewController *addUsersBlogsView = [[AddUsersBlogsViewController alloc] initWithNibName:@"AddUsersBlogsViewController" bundle:nil]; - addUsersBlogsView.isWPcom = NO; - addUsersBlogsView.usersBlogs = subsites; - addUsersBlogsView.url = xmlrpc; - addUsersBlogsView.username = self.username; - addUsersBlogsView.password = self.password; - addUsersBlogsView.geolocationEnabled = self.geolocationEnabled; - [self.navigationController pushViewController:addUsersBlogsView animated:YES]; + [self displayAddUsersBlogsForXmlRpc:xmlRpc]; } else { - NSMutableDictionary *newBlog; - if(subsite) - newBlog = [NSMutableDictionary dictionaryWithDictionary:subsite]; - else - newBlog = [NSMutableDictionary dictionaryWithDictionary:[subsites objectAtIndex:0]]; - [newBlog setObject:self.username forKey:@"username"]; - [newBlog setObject:self.password forKey:@"password"]; - [newBlog setObject:xmlrpc forKey:@"xmlrpc"]; - - self.blog = [Blog createFromDictionary:newBlog withContext:appDelegate.managedObjectContext]; - self.blog.geolocationEnabled = self.geolocationEnabled; - [self.blog dataSave]; - [SVProgressHUD showWithStatus:NSLocalizedString(@"Reading blog options", @"") maskType:SVProgressHUDMaskTypeBlack]; - [self.blog syncBlogWithSuccess:^{ - [[WordPressComApi sharedApi] syncPushNotificationInfo]; - if ([self.blog hasJetpack]) { - NSString *wpcomUsername = [[WordPressComApi sharedApi] username]; - NSString *wpcomPassword = [[WordPressComApi sharedApi] password]; - if (wpcomPassword && wpcomPassword) { - // Try with a known WordPress.com username first - [SVProgressHUD showWithStatus:NSLocalizedString(@"Connecting to Jetpack", @"") maskType:SVProgressHUDMaskTypeBlack]; - [self.blog validateJetpackUsername:wpcomUsername - password:wpcomPassword - success:^{ - [self dismiss]; - } failure:^(NSError *error) { - [self showJetpackAuthentication]; - }]; - } else { - [self showJetpackAuthentication]; - } - } else { - [self dismiss]; - } - } failure:^(NSError *error) { - [SVProgressHUD dismiss]; - }]; + [self createBlogWithXmlRpc:xmlRpc andBlogDetails:subsite]; + [self synchronizeNewlyAddedBlog]; } } else { NSError *error = [NSError errorWithDomain:@"WordPress" code:0 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Sorry, you credentials were good but you don't seem to have access to any blogs", @"")}]; @@ -102,13 +66,75 @@ - (void)validationSuccess:(NSString *)xmlrpc { saveButton.enabled = YES; } +- (void)displayAddUsersBlogsForXmlRpc:(NSString *)xmlRpc +{ + AddUsersBlogsViewController *addUsersBlogsView = [[AddUsersBlogsViewController alloc] init]; + addUsersBlogsView.isWPcom = NO; + addUsersBlogsView.usersBlogs = subsites; + addUsersBlogsView.url = xmlRpc; + addUsersBlogsView.username = self.username; + addUsersBlogsView.password = self.password; + addUsersBlogsView.geolocationEnabled = self.geolocationEnabled; + [self.navigationController pushViewController:addUsersBlogsView animated:YES]; +} + +- (void)createBlogWithXmlRpc:(NSString *)xmlRpc andBlogDetails:(NSDictionary *)blogDetails +{ + NSAssert(blogDetails != nil, nil); + + NSMutableDictionary *newBlog = [NSMutableDictionary dictionaryWithDictionary:blogDetails]; + [newBlog setObject:self.username forKey:@"username"]; + [newBlog setObject:self.password forKey:@"password"]; + [newBlog setObject:xmlRpc forKey:@"xmlrpc"]; + + WordPressAppDelegate *appDelegate = [WordPressAppDelegate sharedWordPressApplicationDelegate]; + self.blog = [Blog createFromDictionary:newBlog withContext:appDelegate.managedObjectContext]; + self.blog.geolocationEnabled = self.geolocationEnabled; + [self.blog dataSave]; +} + +- (void)synchronizeNewlyAddedBlog +{ + void (^successBlock)() = ^{ + [[WordPressComApi sharedApi] syncPushNotificationInfo]; + if ([self.blog hasJetpack]) { + [self connectToJetpack]; + } else { + [self dismiss]; + } + }; + void (^failureBlock)(NSError*) = ^(NSError * error) { + [SVProgressHUD dismiss]; + }; + [SVProgressHUD showWithStatus:NSLocalizedString(@"Reading blog options", @"") maskType:SVProgressHUDMaskTypeBlack]; + [self.blog syncBlogWithSuccess:successBlock failure:failureBlock]; +} + +- (void)connectToJetpack +{ + NSString *wpcomUsername = [WordPressComApi sharedApi].username; + NSString *wpcomPassword = [WordPressComApi sharedApi].password; + if ((wpcomUsername != nil) && (wpcomPassword != nil)) { + // Try with a known WordPress.com username first + [SVProgressHUD showWithStatus:NSLocalizedString(@"Connecting to Jetpack", @"") maskType:SVProgressHUDMaskTypeBlack]; + [self.blog validateJetpackUsername:wpcomUsername + password:wpcomPassword + success:^{ [self dismiss]; } + failure:^(NSError *error) { [self showJetpackAuthentication]; } + ]; + } else { + [self showJetpackAuthentication]; + } +} + - (void)dismiss { [SVProgressHUD dismiss]; if (IS_IPAD) { [self dismissModalViewControllerAnimated:YES]; } - else + else { [self.navigationController popToRootViewControllerAnimated:YES]; + } [[NSNotificationCenter defaultCenter] postNotificationName:@"BlogsRefreshNotification" object:nil]; } diff --git a/WordPress/Classes/Blog.m b/WordPress/Classes/Blog.m index 6d982762591b..9daef8001d5a 100644 --- a/WordPress/Classes/Blog.m +++ b/WordPress/Classes/Blog.m @@ -910,7 +910,7 @@ - (void)mergePages:(NSArray *)newPages { NSMutableArray *pagesToKeep = [NSMutableArray array]; for (NSDictionary *pageInfo in newPages) { - NSNumber *pageID = [[pageInfo objectForKey:@"postid"] numericValue]; + NSNumber *pageID = [[pageInfo objectForKey:@"page_id"] numericValue]; Page *newPage = [Page findOrCreateWithBlog:self andPageID:pageID]; if (newPage.remoteStatus == AbstractPostRemoteStatusSync) { [newPage updateFromDictionary:pageInfo]; diff --git a/WordPress/Classes/EditPostViewController.m b/WordPress/Classes/EditPostViewController.m old mode 100644 new mode 100755 index dfc6a734fff5..0ba3adf0bc81 --- a/WordPress/Classes/EditPostViewController.m +++ b/WordPress/Classes/EditPostViewController.m @@ -63,6 +63,8 @@ @implementation EditPostViewController { AbstractPost *_backupPost; } +#define USE_AUTOSAVES 0 + #pragma mark - #pragma mark LifeCycle Methods @@ -78,8 +80,10 @@ - (id)initWithPost:(AbstractPost *)aPost { self.editMode = EditPostViewControllerModeNewPost; } else { self.editMode = EditPostViewControllerModeEditPost; +#if USE_AUTOSAVES _backupPost = [NSEntityDescription insertNewObjectForEntityForName:[[aPost entity] name] inManagedObjectContext:[aPost managedObjectContext]]; [_backupPost cloneFrom:aPost]; +#endif } } @@ -132,7 +136,8 @@ - (void)viewDidLoad { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(newCategoryCreatedNotificationReceived:) name:WPNewCategoryCreatedAndUpdatedInBlogNotificationName object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(insertMediaAbove:) name:@"ShouldInsertMediaAbove" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(insertMediaBelow:) name:@"ShouldInsertMediaBelow" object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeMedia:) name:@"ShouldRemoveMedia" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeMedia:) name:@"ShouldRemoveMedia" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateMedia:) name:@"UpdateMedia" object:nil]; currentView = editView; writeButton.enabled = NO; @@ -398,7 +403,9 @@ - (void)restoreBackupPost:(BOOL)upload { } - (void)dismissEditView { +#if USE_AUTOSAVES [self deleteBackupPost]; +#endif [self dismissModalViewControllerAnimated:YES]; [[NSNotificationCenter defaultCenter] removeObserver:self]; @@ -565,9 +572,11 @@ - (IBAction)showAddNewCategoryView:(id)sender } - (void)discard { +#if USE_AUTOSAVES if (self.editMode == EditPostViewControllerModeEditPost) { [self restoreBackupPost:NO]; } +#endif [self.apost.original deleteRevision]; //remove the original post in case of local draft unsaved @@ -635,7 +644,11 @@ - (void)autosaveContent { } - (BOOL)canAutosaveRemotely { +#if USE_AUTOSAVES return ((![self.apost.original hasRemote] || [self.apost.original.status isEqualToString:@"draft"]) && self.apost.blog.reachable); +#else + return NO; +#endif } - (BOOL)autosaveRemote { @@ -782,8 +795,12 @@ - (IBAction)cancelView:(id)sender { [titleTextField resignFirstResponder]; [tagsTextField resignFirstResponder]; [self.postSettingsViewController endEditingAction:nil]; +#if USE_AUTOSAVES [self restoreBackupPost:YES]; - if (!self.hasChanges) { +#endif + // If this is a draft with no changes other than a featured image and + // the featured image is still uploading then hasChanges will be false. + if (!self.hasChanges && ![self isMediaInUploading]) { [self discard]; return; } @@ -1228,46 +1245,71 @@ - (void)deviceDidRotate:(NSNotification *)notification { } #pragma mark - Media management +- (void)removeMediaFromContent:(NSMutableString *)content mediaHtml:(NSString *)mediaHtml{ + // find the image html and replace it with an empty string + NSRange imageLocation = [content rangeOfString:[NSString stringWithFormat:@"%@%@", @"

", mediaHtml]]; + if (imageLocation.location == NSNotFound) { + // look at the end of the content + imageLocation = [content rangeOfString:[NSString stringWithFormat:@"%@%@", mediaHtml, @"

"]]; + if (imageLocation.location == NSNotFound) { + // look anywhere in the content + imageLocation = [content rangeOfString:mediaHtml]; + } + } + if (imageLocation.location != NSNotFound) { + [content replaceCharactersInRange:imageLocation withString:@""]; + } +} - (void)insertMediaAbove:(NSNotification *)notification { - Media *media = (Media *)[notification object]; - NSString *prefix = @"

"; + NSDictionary *userInfo = notification.userInfo; + Media *media = (Media *)[userInfo objectForKey:@"media"]; + MediaSettings *mediaSettings = (MediaSettings *)[userInfo objectForKey:@"mediaSettings"]; + NSString *postfix = @"

"; if(self.apost.content == nil || [self.apost.content isEqualToString:@""]) { self.apost.content = @""; - prefix = @""; + postfix = @""; } - NSMutableString *content = [[NSMutableString alloc] initWithString:media.html]; - NSRange imgHTML = [textView.text rangeOfString: content]; - - NSRange imgHTMLPre = [textView.text rangeOfString:[NSString stringWithFormat:@"%@%@", @"

", content]]; - NSRange imgHTMLPost = [textView.text rangeOfString:[NSString stringWithFormat:@"%@%@", content, @"

"]]; + NSString *currentMediaHtml = [MediaSettings createMediaSettingsForUrl:media.remoteURL content:self.apost.content].parsedHtml; + if (currentMediaHtml == nil) { + currentMediaHtml = @""; + } + NSMutableString *content = [[NSMutableString alloc] initWithString:self.apost.content]; + [self removeMediaFromContent:content mediaHtml:currentMediaHtml]; + + self.apost.content = [NSString stringWithFormat:@"%@%@%@", [media htmlWithMediaSettings:mediaSettings], postfix, content]; + [self refreshUIForCurrentPost]; +} + +- (void)updateMedia:(NSNotification *)notification { + NSDictionary *userInfo = notification.userInfo; + Media *media = (Media *)[userInfo objectForKey:@"media"]; + MediaSettings *mediaSettings = (MediaSettings *)[userInfo objectForKey:@"mediaSettings"]; - if (imgHTMLPre.location == NSNotFound && imgHTMLPost.location == NSNotFound && imgHTML.location == NSNotFound) { - [content appendString:[NSString stringWithFormat:@"%@%@", prefix, self.apost.content]]; - self.apost.content = content; + NSMutableString *content = [[NSMutableString alloc] initWithString:self.apost.content]; + + NSString *currentMediaHtml = [MediaSettings createMediaSettingsForUrl:media.remoteURL content:content].parsedHtml; + if (currentMediaHtml == nil) { + currentMediaHtml = @""; + } + NSRange mediaHtmlRange = [content rangeOfString:currentMediaHtml]; + + if (mediaHtmlRange.location == NSNotFound) { + [content appendString:[NSString stringWithFormat:@"

%@", [media htmlWithMediaSettings:mediaSettings]]]; } - else { - NSMutableString *processedText = [[NSMutableString alloc] initWithString:textView.text]; - if (imgHTMLPre.location != NSNotFound) - [processedText replaceCharactersInRange:imgHTMLPre withString:@""]; - else if (imgHTMLPost.location != NSNotFound) - [processedText replaceCharactersInRange:imgHTMLPost withString:@""]; - else - [processedText replaceCharactersInRange:imgHTML withString:@""]; - - [content appendString:[NSString stringWithFormat:@"

%@", processedText]]; - self.apost.content = content; + else { + [content replaceCharactersInRange:mediaHtmlRange withString:[media htmlWithMediaSettings:mediaSettings]]; } - _hasChangesToAutosave = YES; + self.apost.content = content; [self refreshUIForCurrentPost]; - [self.apost autosave]; - [self incrementCharactersChangedForAutosaveBy:content.length]; } - (void)insertMediaBelow:(NSNotification *)notification { - Media *media = (Media *)[notification object]; + NSDictionary *userInfo = notification.userInfo; + Media *media = (Media *)[userInfo objectForKey:@"media"]; + MediaSettings *mediaSettings = (MediaSettings *)[userInfo objectForKey:@"mediaSettings"]; NSString *prefix = @"

"; if(self.apost.content == nil || [self.apost.content isEqualToString:@""]) { @@ -1275,41 +1317,25 @@ - (void)insertMediaBelow:(NSNotification *)notification { prefix = @""; } + NSString *currentMediaHtml = [MediaSettings createMediaSettingsForUrl:media.remoteURL content:self.apost.content].parsedHtml; + if (currentMediaHtml == nil) { + currentMediaHtml = @""; + } NSMutableString *content = [[NSMutableString alloc] initWithString:self.apost.content]; - NSRange imgHTML = [content rangeOfString: media.html]; - NSRange imgHTMLPre = [content rangeOfString:[NSString stringWithFormat:@"%@%@", @"

", media.html]]; - NSRange imgHTMLPost = [content rangeOfString:[NSString stringWithFormat:@"%@%@", media.html, @"

"]]; - - if (imgHTMLPre.location == NSNotFound && imgHTMLPost.location == NSNotFound && imgHTML.location == NSNotFound) { - [content appendString:[NSString stringWithFormat:@"%@%@", prefix, media.html]]; - self.apost.content = content; - } - else { - if (imgHTMLPre.location != NSNotFound) - [content replaceCharactersInRange:imgHTMLPre withString:@""]; - else if (imgHTMLPost.location != NSNotFound) - [content replaceCharactersInRange:imgHTMLPost withString:@""]; - else - [content replaceCharactersInRange:imgHTML withString:@""]; - [content appendString:[NSString stringWithFormat:@"

%@", media.html]]; - self.apost.content = content; - } - _hasChangesToAutosave = YES; + [self removeMediaFromContent:content mediaHtml:currentMediaHtml]; + + self.apost.content = [NSString stringWithFormat:@"%@%@%@", content, prefix, [media htmlWithMediaSettings:mediaSettings]]; [self refreshUIForCurrentPost]; - [self.apost autosave]; - [self incrementCharactersChangedForAutosaveBy:content.length]; } - (void)removeMedia:(NSNotification *)notification { //remove the html string for the media object Media *media = (Media *)[notification object]; - textView.text = [textView.text stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"

%@", media.html] withString:@""]; - textView.text = [textView.text stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%@

", media.html] withString:@""]; - textView.text = [textView.text stringByReplacingOccurrencesOfString:media.html withString:@""]; - _hasChangesToAutosave = YES; - [self autosaveContent]; + NSString *currentMediaHtml = [MediaSettings createMediaSettingsForUrl:media.remoteURL content:self.apost.content].parsedHtml; + NSMutableString *content = [[NSMutableString alloc] initWithString:self.apost.content]; + [self removeMediaFromContent:content mediaHtml:currentMediaHtml]; + self.apost.content = content; [self refreshUIForCurrentPost]; - [self incrementCharactersChangedForAutosaveBy:media.html.length]; } diff --git a/WordPress/Classes/JetpackSettingsViewController.m b/WordPress/Classes/JetpackSettingsViewController.m index fd3fb9e905bd..fcb4e1d7db23 100644 --- a/WordPress/Classes/JetpackSettingsViewController.m +++ b/WordPress/Classes/JetpackSettingsViewController.m @@ -15,6 +15,9 @@ @interface JetpackSettingsViewController () +@property (nonatomic, strong) NSString *username; +@property (nonatomic, strong) NSString *password; + @end @implementation JetpackSettingsViewController { @@ -27,6 +30,9 @@ @implementation JetpackSettingsViewController { BOOL _authenticating; } +@synthesize username = _username; +@synthesize password = _password; + #define kCheckCredentials NSLocalizedString(@"Verify and Save Credentials", @""); #define kCheckingCredentials NSLocalizedString(@"Verifing Credentials", @""); @@ -36,6 +42,8 @@ - (id)initWithBlog:(Blog *)blog { self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { _blog = blog; + self.username = _blog.jetpackUsername; + self.password = _blog.jetpackPassword; } return self; } @@ -85,15 +93,14 @@ - (IBAction)skip:(id)sender { - (IBAction)save:(id)sender { [self dismissKeyboard]; [SVProgressHUD show]; - NSString *username = _usernameCell.textField.text; - NSString *password = _passwordCell.textField.text; + [self setAuthenticating:YES]; - [_blog validateJetpackUsername:username - password:password + [_blog validateJetpackUsername:_username + password:_password success:^{ [SVProgressHUD dismiss]; if (![[WordPressComApi sharedApi] username]) { - [[WordPressComApi sharedApi] signInWithUsername:username password:password success:nil failure:nil]; + [[WordPressComApi sharedApi] signInWithUsername:_username password:_password success:nil failure:nil]; } [self setAuthenticating:NO]; if (self.completionBlock) { @@ -162,7 +169,7 @@ - (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexP textCell.textField.keyboardType = UIKeyboardTypeEmailAddress; textCell.shouldDismissOnReturn = NO; textCell.delegate = self; - textCell.textField.text = _blog.jetpackUsername; + textCell.textField.text = _username; _usernameCell = textCell; } else { textCell.textLabel.text = NSLocalizedString(@"Password:", @""); @@ -170,7 +177,7 @@ - (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexP textCell.textField.secureTextEntry = YES; textCell.shouldDismissOnReturn = YES; textCell.delegate = self; - textCell.textField.text = _blog.jetpackPassword; + textCell.textField.text = _password; _passwordCell = textCell; } cell = textCell; @@ -235,6 +242,11 @@ - (void)cellWantsToSelectNextField:(UITableViewTextFieldCell *)cell { } - (void)cellTextDidChange:(UITableViewTextFieldCell *)cell { + if([cell isEqual:_usernameCell]) { + self.username = _usernameCell.textField.text; + } else { + self.password = _passwordCell.textField.text; + } [self updateSaveButton]; } @@ -254,6 +266,8 @@ - (void)setAuthenticating:(BOOL)authenticating { } - (void)updateSaveButton { + if (![self isViewLoaded]) return; + if ([self useNavigationController]) { self.navigationItem.rightBarButtonItem.enabled = [self saveEnabled]; } else { @@ -313,7 +327,7 @@ - (void)tryLoginWithCurrentWPComCredentials { if ([_blog hasJetpack] && !([[_blog jetpackUsername] length] && [[_blog jetpackPassword] length])) { NSString *wpcomUsername = [[WordPressComApi sharedApi] username]; NSString *wpcomPassword = [[WordPressComApi sharedApi] password]; - if (wpcomPassword && wpcomPassword) { + if (wpcomUsername && wpcomPassword) { [self tryLoginWithUsername:wpcomUsername andPassword:wpcomPassword]; } } @@ -324,6 +338,10 @@ - (void)tryLoginWithUsername:(NSString *)username andPassword:(NSString *)passwo NSAssert(password != nil, @"Can't login with a nil password"); _usernameCell.textField.text = username; _passwordCell.textField.text = password; + + self.username = username; + self.password = password; + [self save:nil]; } diff --git a/WordPress/Classes/Media.h b/WordPress/Classes/Media.h old mode 100644 new mode 100755 index 9206c2053734..11a7048627f5 --- a/WordPress/Classes/Media.h +++ b/WordPress/Classes/Media.h @@ -9,6 +9,7 @@ #import #import "Blog.h" #import "AbstractPost.h" +#import "MediaSettings.h" typedef NS_ENUM(NSUInteger, MediaRemoteStatus) { MediaRemoteStatusPushing, // Uploading post @@ -51,6 +52,7 @@ typedef NS_ENUM(NSUInteger, MediaRemoteStatus) { - (void)remove; - (void)save; - (void)setImage:(UIImage *)image withSize:(MediaResize)size; +- (NSString *)htmlWithMediaSettings:(MediaSettings *)mediaSettings; @end diff --git a/WordPress/Classes/Media.m b/WordPress/Classes/Media.m old mode 100644 new mode 100755 index 237e0232a707..f6e4f700d84d --- a/WordPress/Classes/Media.m +++ b/WordPress/Classes/Media.m @@ -143,11 +143,27 @@ - (void)xmlrpcUploadWithSuccess:(void (^)())success failure:(void (^)(NSError *e NSMutableURLRequest *request = [self.blog.api requestWithMethod:@"metaWeblog.newMediaObject" parameters:parameters]; dispatch_async(dispatch_get_main_queue(), ^(void) { + void (^failureBlock)(AFHTTPRequestOperation *, NSError *) = ^(AFHTTPRequestOperation *operation, NSError *error) { + if ([self.mediaType isEqualToString:@"featured"]) { + [[NSNotificationCenter defaultCenter] postNotificationName:FeaturedImageUploadFailed + object:self]; + } + + self.remoteStatus = MediaRemoteStatusFailed; + _uploadOperation = nil; + if (failure) failure(error); + }; AFHTTPRequestOperation *operation = [self.blog.api HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { if ([self isDeleted] || self.managedObjectContext == nil) return; NSDictionary *response = (NSDictionary *)responseObject; + + if (![response isKindOfClass:[NSDictionary class]]) { + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadServerResponse userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"The server returned an empty response. This usually means you need to increase the memory limit in your blog", @"")}]; + failureBlock(operation, error); + return; + } if([response objectForKey:@"videopress_shortcode"] != nil) self.shortcode = [response objectForKey:@"videopress_shortcode"]; @@ -178,15 +194,6 @@ - (void)xmlrpcUploadWithSuccess:(void (^)())success failure:(void (^)(NSError *e } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if ([self isDeleted] || self.managedObjectContext == nil) return; - - if ([self.mediaType isEqualToString:@"featured"]) { - [[NSNotificationCenter defaultCenter] postNotificationName:FeaturedImageUploadFailed - object:self]; - } - - self.remoteStatus = MediaRemoteStatusFailed; - _uploadOperation = nil; - if (failure) failure(error); }]; [operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) { dispatch_async(dispatch_get_main_queue(), ^(void) { @@ -207,6 +214,13 @@ - (void)xmlrpcUploadWithSuccess:(void (^)())success failure:(void (^)(NSError *e } - (NSString *)html { + return [self htmlWithMediaSettings:nil]; +} + +- (NSString *)htmlWithMediaSettings:(MediaSettings *)mediaSettings { + if (mediaSettings == nil) { + mediaSettings = [[MediaSettings alloc] init]; + } NSString *result = @""; if(self.mediaType != nil) { @@ -214,20 +228,30 @@ - (NSString *)html { if(self.shortcode != nil) result = self.shortcode; else if(self.remoteURL != nil) { - NSString *linkType = nil; - if( [[self.blog getOptionValue:@"image_default_link_type"] isKindOfClass:[NSString class]] ) - linkType = (NSString *)[self.blog getOptionValue:@"image_default_link_type"]; - else - linkType = @""; + // try to generate the HTML from the media settings first + result = [mediaSettings html]; - if ([linkType isEqualToString:@"none"]) { - result = [NSString stringWithFormat: - @"\"%@\"", - self.remoteURL, self.filename]; - } else { + // if the HTML generated was empty then create the default HTML for the image + if (result == nil || [result isEqualToString:@""]) { + // set the link blocks + NSString *linkPrefix = nil; + NSString *linkPostfix = nil; + if([[self.blog getOptionValue:@"image_default_link_type"] isKindOfClass:[NSString class]]) { + NSString *mediaLinkType = (NSString *)[self.blog getOptionValue:@"image_default_link_type"]; + if ([mediaLinkType isEqualToString:@"file"]) { + linkPrefix = [NSString stringWithFormat: + @"", + self.remoteURL]; + linkPostfix = @""; + } + } else { + linkPrefix = @""; + linkPostfix = @""; + } + result = [NSString stringWithFormat: - @"\"%@\"", - self.remoteURL, self.remoteURL, self.filename]; + @"%@%@", + linkPrefix, self.remoteURL, linkPostfix]; } } } diff --git a/WordPress/Classes/MediaObjectViewController.m b/WordPress/Classes/MediaObjectViewController.m index c1a15f39058e..33903ccca659 100644 --- a/WordPress/Classes/MediaObjectViewController.m +++ b/WordPress/Classes/MediaObjectViewController.m @@ -143,16 +143,17 @@ - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger } } else if(isInserting == YES) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:media, @"media", [[MediaSettings alloc] init], @"mediaSettings", nil]; switch (buttonIndex) { case 0: - [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaAbove" object:media]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaAbove" object:self userInfo:userInfo]; if(IS_IPAD == YES) [self dismissModalViewControllerAnimated:YES]; else [self.navigationController popViewControllerAnimated:YES]; break; case 1: - [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:media]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:self userInfo:userInfo]; if(IS_IPAD == YES) [self dismissModalViewControllerAnimated:YES]; else diff --git a/WordPress/Classes/MediaSettings.h b/WordPress/Classes/MediaSettings.h new file mode 100644 index 000000000000..bde1f1921eb5 --- /dev/null +++ b/WordPress/Classes/MediaSettings.h @@ -0,0 +1,29 @@ +// +// MediaSettings.h +// WordPress +// +// Created by Jeffrey Vanneste on 2013-01-12. +// Copyright (c) 2013 WordPress. All rights reserved. +// + +#import + +@interface MediaSettings : NSObject + +// creates a MediaSettings object for the first occurrence of the URL in the content ++ (MediaSettings *)createMediaSettingsForUrl:(NSString *)url content:(NSString *)content; +- (NSString *) html; + +@property (nonatomic, strong, readonly) NSString* parsedHtml; +@property (nonatomic, strong, readonly) NSString* parsedCaptionAttributes; +@property (nonatomic, strong, readonly) NSString* parsedImageHtml; +@property (nonatomic, strong, readonly) NSString* parsedAnchorHtml; + +@property (nonatomic, strong) NSString* linkHref; +@property (nonatomic, strong) NSString* captionText; +@property (nonatomic, strong) NSString* alignment; +@property (nonatomic, strong) NSNumber* customWidth; +@property (nonatomic, strong) NSNumber* customHeight; + +@end + diff --git a/WordPress/Classes/MediaSettings.m b/WordPress/Classes/MediaSettings.m new file mode 100644 index 000000000000..533001a896fe --- /dev/null +++ b/WordPress/Classes/MediaSettings.m @@ -0,0 +1,315 @@ +// +// MediaSettings.m +// WordPress +// +// Created by Jeffrey Vanneste on 2013-01-12. +// Copyright (c) 2013 WordPress. All rights reserved. +// + +#import "MediaSettings.h" + +@interface MediaSettings () +@property (nonatomic, strong) NSString* parsedHtml; +@property (nonatomic, strong) NSString* parsedCaptionAttributes; +@property (nonatomic, strong) NSString* parsedImageHtml; +@property (nonatomic, strong) NSString* parsedAnchorHtml; +@end + + +@implementation MediaSettings {} + +@synthesize parsedHtml; +@synthesize parsedImageHtml; +@synthesize parsedCaptionAttributes; +@synthesize parsedAnchorHtml; +@synthesize linkHref; +@synthesize captionText; +@synthesize customWidth; +@synthesize customHeight; +@synthesize alignment; + +- (void)dealloc { + parsedHtml = nil; + parsedImageHtml = nil; + parsedCaptionAttributes = nil; + parsedAnchorHtml = nil; + linkHref = nil; + captionText = nil; + customWidth = nil; + customHeight = nil; + alignment = nil; +} + + +- (NSString *) html { + NSMutableString *html = [NSMutableString string]; + + // MediaSettings can only generate html if existing parsed html exists + if (parsedHtml == nil || [parsedHtml isEqualToString:@""] || parsedImageHtml == nil || [parsedImageHtml isEqualToString:@""]) { + return @""; + } + + // create the caption blocks + NSString *captionPrefix = nil; + NSString *captionPostfix = nil; + if (captionText != nil && ![captionText isEqualToString:@""]) { + NSString *captionAttributes = @""; + if (parsedCaptionAttributes == nil || [parsedCaptionAttributes isEqualToString:@""]) { + captionAttributes = @""; + } else { + captionAttributes = [NSString stringWithString:parsedCaptionAttributes]; + } + if (alignment != nil) { + captionAttributes = [self replaceOrSetValueInContent:captionAttributes regex:@"align=['\"]([^'\"]*)['\"]?" replacement:[NSString stringWithFormat:@"align=\"%@\"", alignment]]; + } + if (customWidth != nil) { + captionAttributes = [self replaceOrSetValueInContent:captionAttributes regex:@"width=['\"]([0-9]*)['\"]?" replacement:[NSString stringWithFormat:@"width=\"%d\"", [customWidth intValue]]]; + } + captionPrefix = [NSString stringWithFormat:@"[caption%@]", captionAttributes]; + captionPostfix = [NSString stringWithFormat:@"%@[/caption]", captionText]; + } else { + captionPrefix = @""; + captionPostfix = @""; + } + + // create the link blocks + NSString *linkPrefix = @""; + NSString *linkPostfix = @""; + if (linkHref != nil) { + if (![linkHref isEqualToString:@""]) { + if (parsedAnchorHtml == nil || [parsedAnchorHtml isEqualToString:@""]) { + linkPrefix = [NSString stringWithFormat:@"", linkHref]; + } else { + // temporary remove the trailing > + linkPrefix = [parsedAnchorHtml substringWithRange:NSMakeRange(0, parsedAnchorHtml.length-1)]; + linkPrefix = [NSString stringWithFormat:@"%@>", [self replaceOrSetValueInContent:linkPrefix regex:@"href=['\"]([^'\"]*)['\"]?" replacement:[NSString stringWithFormat:@"href=\"%@\"", linkHref]]]; + } + linkPostfix = @""; + } + } + + // update the image html + NSString *imageHtml = [NSString stringWithString:parsedImageHtml]; + // temporary remove the trailing /> + imageHtml = [imageHtml substringWithRange:NSMakeRange(0, imageHtml.length-2)]; + if (customWidth != nil) { + imageHtml = [self replaceOrSetValueInContent:imageHtml regex:@"width=['\"]([0-9]*)['\"]?" replacement:[NSString stringWithFormat:@"width=\"%d\"", [customWidth intValue]]]; + } + if (customHeight != nil) { + imageHtml = [self replaceOrSetValueInContent:imageHtml regex:@"height=['\"]([0-9]*)['\"]?" replacement:[NSString stringWithFormat:@"height=\"%d\"", [customHeight intValue]]]; + } + if (alignment != nil) { + // add the alignment to the img class field unless it was added to the caption shortcode + NSString *imageClass = [MediaSettings parseContent:imageHtml regex:@"class=['\"]([^'\"]*)['\"]?" retrievalGroup:1]; + NSMutableArray *imageClasses = [NSMutableArray arrayWithArray:[imageClass componentsSeparatedByString:@" "]]; + [imageClasses removeObject:@""]; + [imageClasses removeObject:@"alignnone"]; + [imageClasses removeObject:@"alignleft"]; + [imageClasses removeObject:@"alignright"]; + [imageClasses removeObject:@"aligncenter"]; + if (captionText == nil || [captionText isEqualToString:@""]) { + [imageClasses addObject:alignment]; + } + if ([imageClasses count] > 0) { + imageClass = [imageClasses componentsJoinedByString:@" "]; + imageHtml = [self replaceOrSetValueInContent:imageHtml regex:@"class=['\"]([^'\"]*)['\"]?" replacement:[NSString stringWithFormat:@"class=\"%@\"", imageClass]]; + } + } + imageHtml = [NSString stringWithFormat:@"%@/>", imageHtml]; + + [html appendFormat:@"%@%@%@%@%@", captionPrefix, linkPrefix, imageHtml, linkPostfix, captionPostfix]; + + return html; +} + +- (NSString *)description { + NSMutableString *result = [[NSMutableString alloc] init]; + [result appendFormat:@"parsedHtml: %@\n", parsedHtml]; + [result appendFormat:@"parsedAnchorHtml: %@\n", parsedAnchorHtml]; + [result appendFormat:@"parsedImageHtml: %@\n", parsedImageHtml]; + [result appendFormat:@"parsedCaptionAttributes: %@\n", parsedCaptionAttributes]; + [result appendFormat:@"linkHref: %@\n", linkHref]; + [result appendFormat:@"captionText: %@\n", captionText]; + [result appendFormat:@"customWidth: %@\n", customWidth]; + [result appendFormat:@"customHeight: %@\n", customHeight]; + [result appendFormat:@"alignment: %@\n", alignment]; + + [result appendFormat:@"html: %@\n", [self html]]; + return result; +} + +- (NSString *)replaceOrSetValueInContent:(NSString *)content regex:(NSString *)regex replacement:(NSString *)replacement { + NSError* error = nil; + NSRegularExpression* regexExpression = [NSRegularExpression + regularExpressionWithPattern:regex + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSArray* matches = [regexExpression matchesInString:content options:NSRegularExpressionCaseInsensitive range:NSMakeRange(0, [content length])]; + for (NSTextCheckingResult* match in matches) { + if ([match numberOfRanges] > 0) { + NSMutableString *newContent = [NSMutableString stringWithString:content]; + [newContent replaceCharactersInRange:[match rangeAtIndex:0] withString:replacement]; + return newContent; + } + } + + // if no match is found return the same content + return [NSString stringWithFormat:@"%@ %@", content, replacement]; +} + +#pragma mark - +#pragma mark - Class methods ++ (NSString *)parseContent:(NSString *)content regex:(NSString *)regex retrievalGroup:(int)retrievalGroup { + NSError* error = nil; + NSRegularExpression* regexExpression = [NSRegularExpression + regularExpressionWithPattern:regex + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSArray* matches = [regexExpression matchesInString:content options:NSRegularExpressionCaseInsensitive range:NSMakeRange(0, [content length])]; + for (NSTextCheckingResult* match in matches) { + if ([match numberOfRanges] > retrievalGroup) { + return [content substringWithRange:[match rangeAtIndex:retrievalGroup]]; + } + } + + // if nothing is found + return @""; +} + ++ (MediaSettings *)createMediaSettingsForUrlHelper:(NSString *)url content:(NSString *)content includeCaption:(BOOL)includeCaption mediaSettings:(MediaSettings *)mediaSettings { + if (mediaSettings == nil) { + mediaSettings = [[MediaSettings alloc] init]; + } + + // This will match every [caption...] ... [/caption] and create 3 groups: + // Group0: the whole match + // Group1: html containing link and img or just img if there is no link + // Group2: the img html + // Group3: the caption if includeCaption is set + NSError* error = nil; + NSRegularExpression* fullMediaRegex; + if (includeCaption) { + fullMediaRegex = [NSRegularExpression + regularExpressionWithPattern:@"((?:]+>)?(]+>)(?:<\\/a>)?)([\\s\\S]*)" + options:NSRegularExpressionCaseInsensitive + error:&error]; + } else { + fullMediaRegex = [NSRegularExpression + regularExpressionWithPattern:@"((?:]+>)?(]+>)(?:<\\/a>)?)" + options:NSRegularExpressionCaseInsensitive + error:&error]; + } + + // find the first match of url in a media match + NSArray* matches = [fullMediaRegex matchesInString:content options:NSRegularExpressionCaseInsensitive range:NSMakeRange(0, [content length])]; + for (NSTextCheckingResult* match in matches) { + NSString* matchText = [content substringWithRange:[match range]]; + if ([matchText rangeOfString:url options:NSCaseInsensitiveSearch].location != NSNotFound) { + if ([match numberOfRanges] >= 3) { + // text containing the link and img + NSString *imageHtml = [content substringWithRange:[match rangeAtIndex:2]]; + mediaSettings.parsedImageHtml = imageHtml; + NSString *imageSource = [self parseContent:imageHtml regex:@"src=['\"]([^'\"]*)['\"]?" retrievalGroup:1]; + if ([imageSource caseInsensitiveCompare:url] != NSOrderedSame) { + // the image source tag does not match the url passed in so look at the next match + continue; + } + NSString *parsedWidth = [self parseContent:imageHtml regex:@"width=['\"]([0-9]*)['\"]?" retrievalGroup:1]; + if (parsedWidth != nil && ![parsedWidth isEqualToString:@""]) { + mediaSettings.customWidth = [NSNumber numberWithInt:[parsedWidth intValue]]; + } + NSString *parsedHeight = [self parseContent:imageHtml regex:@"height=['\"]([0-9]*)['\"]?" retrievalGroup:1]; + if (parsedHeight != nil && ![parsedHeight isEqualToString:@""]) { + mediaSettings.customHeight = [NSNumber numberWithInt:[parsedHeight intValue]]; + } + if (mediaSettings.alignment == nil) { + NSString *imageClasses = [self parseContent:imageHtml regex:@"class=['\"]([^'\"]*)['\"]?" retrievalGroup:1]; + if ([imageClasses rangeOfString:@"alignleft" options:NSCaseInsensitiveSearch].location != NSNotFound) { + mediaSettings.alignment = @"alignleft"; + } else if ([imageClasses rangeOfString:@"aligncenter" options:NSCaseInsensitiveSearch].location != NSNotFound) { + mediaSettings.alignment = @"aligncenter"; + } else if ([imageClasses rangeOfString:@"alignright" options:NSCaseInsensitiveSearch].location != NSNotFound) { + mediaSettings.alignment = @"alignright"; + } else { + mediaSettings.alignment = @"alignnone"; + } + } + } + if ([match numberOfRanges] >= 2) { + // text containing the link and img + NSString *linkAndImageHtml = [content substringWithRange:[match rangeAtIndex:1]]; + if (!includeCaption) { + mediaSettings.parsedHtml = linkAndImageHtml; + } + + mediaSettings.linkHref = [self parseContent:linkAndImageHtml regex:@"href=['\"]([^'\"]*)['\"]?" retrievalGroup:1]; + if (mediaSettings.linkHref != nil && ![mediaSettings.linkHref isEqualToString:@""]) { + NSRange imageHtmlRange = [linkAndImageHtml rangeOfString:mediaSettings.parsedImageHtml]; + + mediaSettings.parsedAnchorHtml = [linkAndImageHtml substringWithRange:NSMakeRange(0, imageHtmlRange.location)];; + } + } + if (includeCaption && [match numberOfRanges] >= 4) { + mediaSettings.captionText = [content substringWithRange:[match rangeAtIndex:3]]; + } + + break; + } + } + + return mediaSettings; +} + ++ (MediaSettings *)createMediaSettingsForUrl:(NSString *)url content:(NSString *)content { + MediaSettings *mediaSettings = [[MediaSettings alloc] init]; + + // This will match every [caption...] ... [/caption] and create 3 groups: + // Group0: the whole match + // Group1: the caption attributes + // Group2: the a and img elements and the caption string + NSError* error = nil; + NSRegularExpression* fullMediaRegex = [NSRegularExpression + regularExpressionWithPattern:@"(?:

)?\\[(?:wp_)?caption([^\\]]+)\\]([\\s\\S]+?)\\[\\/(?:wp_)?caption\\](?:<\\/p>)?" + options:NSRegularExpressionCaseInsensitive + error:&error]; + + // find the first match of url in a media match + BOOL mediaFound = NO; + NSArray* matches = [fullMediaRegex matchesInString:content options:NSRegularExpressionCaseInsensitive range:NSMakeRange(0, [content length])]; + for (NSTextCheckingResult* match in matches) { + NSString* matchText = [content substringWithRange:[match range]]; + if ([matchText rangeOfString:url options:NSCaseInsensitiveSearch].location != NSNotFound) { + mediaSettings.parsedHtml = matchText; + if ([match numberOfRanges] >= 1) { + + // get all the attributes for the caption + NSString *captionAttributes = [content substringWithRange:[match rangeAtIndex:1]]; + + mediaSettings.parsedCaptionAttributes = captionAttributes; + NSString *parsedWidth = [self parseContent:captionAttributes regex:@"width=['\"]([0-9]*)['\"]?" retrievalGroup:1]; + if (parsedWidth != nil && ![parsedWidth isEqualToString:@""]) { + mediaSettings.customWidth = [NSNumber numberWithInt:[parsedWidth intValue]]; + } + mediaSettings.alignment = [[self parseContent:captionAttributes regex:@"align=['\"]([^'\"]*)['\"]?" retrievalGroup:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + } + if ([match numberOfRanges] >= 2) { + NSString *imageHtmlWithCaption = [content substringWithRange:[match rangeAtIndex:2]]; + [self createMediaSettingsForUrlHelper:url content:imageHtmlWithCaption includeCaption:YES mediaSettings:mediaSettings]; + } + + mediaFound = YES; + break; + } + } + + if (!mediaFound) { + // if the image didn't have a caption we need to do another search + [self createMediaSettingsForUrlHelper:url content:content includeCaption:NO mediaSettings:mediaSettings]; + } + + return mediaSettings; +} + +@end + + diff --git a/WordPress/Classes/MediaSettingsViewController.h b/WordPress/Classes/MediaSettingsViewController.h new file mode 100755 index 000000000000..fc28a8ec9e5b --- /dev/null +++ b/WordPress/Classes/MediaSettingsViewController.h @@ -0,0 +1,43 @@ +// +// MediaSettingsViewController.h +// WordPress +// +// Created by Jeffrey Vanneste on 2013-01-05. +// Copyright (c) 2013 WordPress. All rights reserved. +// + +#import +#import +#import "WordPressAppDelegate.h" +#import "Media.h" +#import "UITableViewActivityCell.h" +#import "WPPopoverBackgroundView.h" +#import "MediaSettings.h" + +@interface MediaSettingsViewController : UIViewController { + Media *media; + NSArray *linkToOptionsList, *positioningOptionsList, *alignmentOptionsList; + BOOL isShowingKeyboard; + + UIPickerView *pickerView; + UIToolbar *toolbar; + UIActionSheet *currentActionSheet; + UIActionSheet *actionSheet; + UIView *footerView; + UIButton *deleteButton; + IBOutlet UIBarButtonItem *cancelButton; + UIPopoverController *popover; + + IBOutlet UITableView *tableView; + IBOutlet UITableViewCell *linkToTableViewCell, *captionTableViewCell, *widthTableViewCell, *alignmentTableViewCell, *positioningTableViewCell, *mediaTableViewCell; + IBOutlet UILabel *linkToLabel, *alignmentLabel, *positioningLabel, *imageSizeLabel, + *linkToTitleLabel, *captionTitleLabel, *widthTitleLabel, *alignmentTitleLabel, *positioningTitleLabel; + IBOutlet UIImageView *thumbnail; + IBOutlet UITextField *captionTextField; + IBOutlet UISlider *widthSlider; +} + +@property (nonatomic, strong) Media *media; +@property (nonatomic, strong) MediaSettings *mediaSettings; + +@end diff --git a/WordPress/Classes/MediaSettingsViewController.m b/WordPress/Classes/MediaSettingsViewController.m new file mode 100755 index 000000000000..8b2317197b1d --- /dev/null +++ b/WordPress/Classes/MediaSettingsViewController.m @@ -0,0 +1,643 @@ +// +// MediaSettingsViewController.m +// WordPress +// +// Created by Jeffrey Vanneste on 2013-01-05. +// Copyright (c) 2013 WordPress. All rights reserved. +// + +#import "MediaSettingsViewController.h" + + +#define TAG_PICKER_LINK_TO 0 +#define TAG_PICKER_ALIGNMENT 1 +#define TAG_PICKER_POSITIONING 2 + +@implementation MediaSettingsViewController + +@synthesize media; +@synthesize mediaSettings; + +- (void)viewDidLoad { + [super viewDidLoad]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; + + // fill the lists with the available options + linkToOptionsList = [NSArray arrayWithObjects: + NSLocalizedString(@"No Linking", @"Media Settings option to have media have no link"), + NSLocalizedString(@"Media File", @"Media Settings option to have media link to the file"), + NSLocalizedString(@"Current", @"Media Settings option to leave the media alone"), nil]; + positioningOptionsList = [NSArray arrayWithObjects: + NSLocalizedString(@"Current", @"Media Settings option to leave the media alone"), + NSLocalizedString(@"Above Content", @"Media Settings option to move media above the content"), + NSLocalizedString(@"Below Content", @"Media Settings option to move media below the content"), nil]; + alignmentOptionsList = [NSArray arrayWithObjects: + NSLocalizedString(@"None", @"Media Settings option to have no alignment"), + NSLocalizedString(@"Left", @"Media Settings option to align the media to left"), + NSLocalizedString(@"Center", @"Media Settings option to align the media to center"), + NSLocalizedString(@"Right", @"Media Settings option to align the media to right"), nil]; + + if (mediaSettings.captionText != nil) { + captionTextField.text = mediaSettings.captionText; + } else { + captionTextField.text = @""; + } + if (mediaSettings.linkHref!= nil) { + if ([mediaSettings.linkHref caseInsensitiveCompare:media.remoteURL] == NSOrderedSame) { + linkToLabel.text = [linkToOptionsList objectAtIndex:1]; + } else if ([mediaSettings.linkHref length] > 0) { + linkToLabel.text = [linkToOptionsList objectAtIndex:2]; + } else { + linkToLabel.text = [linkToOptionsList objectAtIndex:0]; + } + } else { + linkToLabel.text = [linkToOptionsList objectAtIndex:0]; + } + if (mediaSettings.alignment != nil) { + if ([mediaSettings.alignment isEqualToString:@"alignleft"]) { + alignmentLabel.text = [alignmentOptionsList objectAtIndex:1]; + } else if ([mediaSettings.alignment isEqualToString:@"aligncenter"]) { + alignmentLabel.text = [alignmentOptionsList objectAtIndex:2]; + } else if ([mediaSettings.alignment isEqualToString:@"alignright"]) { + alignmentLabel.text = [alignmentOptionsList objectAtIndex:3]; + } else { + alignmentLabel.text = [alignmentOptionsList objectAtIndex:0]; + } + } else { + alignmentLabel.text = [alignmentOptionsList objectAtIndex:0]; + } + positioningLabel.text = [positioningOptionsList objectAtIndex:0]; + + widthSlider.minimumValue = 0; + widthSlider.maximumValue = [media.width intValue]; + if (mediaSettings.customWidth != nil) { + int customWidth = [mediaSettings.customWidth intValue]; + if (customWidth > widthSlider.maximumValue) { + widthSlider.maximumValue = customWidth; + } + int customHeight = customWidth * [media.height intValue]/[media.width intValue]; + imageSizeLabel.text = [NSString stringWithFormat:@"%d x %d", customWidth, customHeight]; + widthSlider.value = customWidth; + } else { + imageSizeLabel.text = [NSString stringWithFormat:@"%d x %d", [media.width intValue], [media.height intValue]]; + widthSlider.value = [media.width intValue]; + } + + [tableView setBackgroundView:nil]; + [tableView setBackgroundColor:[UIColor clearColor]]; //Fix for black corners on iOS4. http://stackoverflow.com/questions/1557856/black-corners-on-uitableview-group-style + self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"settings_bg"]]; + + linkToTitleLabel.text = NSLocalizedString(@"Link to", @"Media Settings for what an image/view should link to"); + positioningTitleLabel.text = NSLocalizedString(@"Position", @"Media Settings for where an image/view should be positioned"); + alignmentTitleLabel.text = NSLocalizedString(@"Alignment", @"Media Settings for where an image/view should be aligned"); + captionTitleLabel.text = NSLocalizedString(@"Caption", @"Media Settings for the caption of an image/view"); + widthTitleLabel.text = NSLocalizedString(@"Width", @"Media Settings for the width of image/video"); + captionTextField.placeholder = NSLocalizedString(@"Optional", @"Media Settings for the optional caption"); + + // only supporting images right now + if ([media.mediaType isEqualToString:@"image"]) { + thumbnail.image = [UIImage imageWithContentsOfFile:media.localURL]; + if((thumbnail.image == nil) && (media.remoteURL != nil)) { + [thumbnail setImageWithURL:[NSURL URLWithString:media.remoteURL]]; + } + self.navigationItem.title = NSLocalizedString(@"Image", @""); + } + + isShowingKeyboard = NO; + + CGRect pickerFrame; + if (IS_IPAD) + pickerFrame = CGRectMake(0.0f, 0.0f, 320.0f, 216.0f); + else + pickerFrame = CGRectMake(0.0f, 44.0f, 320.0f, 216.0f); + pickerView = [[UIPickerView alloc] initWithFrame:pickerFrame]; + pickerView.delegate = self; + pickerView.dataSource = self; + pickerView.showsSelectionIndicator = YES; + + // on the ipad we want to show a Close button in the toolbar since the back button will not be there + if (IS_IPAD) { + cancelButton.title = NSLocalizedString(@"Close", @"Close an action sheet");; + CGRect rect = tableView.frame; + rect.origin.y = 44.0f; + rect.size.height = rect.size.height - 44.0f; + tableView.frame = rect; + + UIToolbar *topToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 44.0f)]; + topToolbar.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth; + UIBarButtonItem *flex = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; + topToolbar.items = [NSArray arrayWithObjects:flex, cancelButton, nil]; + [self.view addSubview:topToolbar]; + } +} + +- (void)viewDidUnload { + [super viewDidUnload]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + [linkToTableViewCell becomeFirstResponder]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; +} + +#pragma mark - +#pragma mark Misc Methods +- (void)deleteObject:(id)sender { + // On ipad show an alert view like how Apple does in the contacts app. On the iPhone show an action sheet. + if (IS_IPAD) { + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Remove %@?", @""), media.mediaTypeName] message:@"" delegate:self cancelButtonTitle:NSLocalizedString(@"Cancel", @"") otherButtonTitles:nil]; + [alert addButtonWithTitle:NSLocalizedString(@"Remove", @"")]; + [alert show]; + } else { + NSString *titleString = [NSString stringWithFormat:NSLocalizedString(@"Remove %@?", @""), media.mediaTypeName]; + UIActionSheet *deleteActionSheet = [[UIActionSheet alloc] initWithTitle:titleString + delegate:self + cancelButtonTitle:NSLocalizedString(@"Cancel", @"") + destructiveButtonTitle:NSLocalizedString(@"Remove", @"") + otherButtonTitles:nil]; + + [deleteActionSheet showInView:self.view]; + } +} + +- (void)reloadData { + [tableView reloadData]; +} + + +#pragma mark - +#pragma mark TableView Methods +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + NSInteger sections = 1; + return sections; +} + +- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + switch (indexPath.section) { + case 0: + switch (indexPath.row) { + case 0: // media + mediaTableViewCell.selectionStyle = UITableViewCellSelectionStyleNone; + return mediaTableViewCell; + break; + case 1: // caption + captionTableViewCell.selectionStyle = UITableViewCellSelectionStyleNone; + return captionTableViewCell; + break; + case 2: // link to + linkToTableViewCell.selectionStyle = UITableViewCellSelectionStyleNone; + return linkToTableViewCell; + break; + case 3: // alignment + alignmentTableViewCell.selectionStyle = UITableViewCellSelectionStyleNone; + return alignmentTableViewCell; + break; + case 4: // positioning + positioningTableViewCell.selectionStyle = UITableViewCellSelectionStyleNone; + return positioningTableViewCell; + break; + case 5: // width + widthTableViewCell.selectionStyle = UITableViewCellSelectionStyleNone; + return widthTableViewCell; + break; + default: + break; + } + break; + } + + return nil; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + if ((indexPath.section == 0) && (indexPath.row == 5)) { + // width row + return 70.0f; + } else if ((indexPath.section == 0) && (indexPath.row == 0)) { + // thumbnail row + return 188.0f; + } + else { + return 44.0f; + } +} + +- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { + return 70; +} + +- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { + + if(footerView == nil) { + footerView = [[UIView alloc] init]; + + // create a glossy red button + UIImage *image = [[UIImage imageNamed:@"button_red.png"] + stretchableImageWithLeftCapWidth:8 topCapHeight:8]; + deleteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [deleteButton setBackgroundImage:image forState:UIControlStateNormal]; + + if (IS_IPAD) { + [deleteButton setFrame:CGRectMake(30, 20, 480, 44)]; + } + else { + [deleteButton setFrame:CGRectMake(10, 20, 300, 44)]; + } + + [deleteButton setTitle:[NSString stringWithFormat:NSLocalizedString(@"Remove %@", @""), media.mediaTypeName] forState:UIControlStateNormal]; + [deleteButton.titleLabel setFont:[UIFont boldSystemFontOfSize:20]]; + [deleteButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [deleteButton addTarget:self action:@selector(deleteObject:) + forControlEvents:UIControlEventTouchUpInside]; + [footerView addSubview:deleteButton]; + } + + return footerView; +} + + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + if (section == 0) { + return 6; + } + return 0; +} + +- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + if (isShowingKeyboard) { + [captionTextField resignFirstResponder]; + } + + switch (indexPath.section) { + case 0: + switch (indexPath.row) { + case 2: + { + pickerView.tag = TAG_PICKER_LINK_TO; + [pickerView reloadAllComponents]; + [pickerView selectRow:[linkToOptionsList indexOfObject:linkToLabel.text] inComponent:0 animated:NO]; + [self showPicker:pickerView]; + break; + } + case 3: + { + pickerView.tag = TAG_PICKER_ALIGNMENT; + [pickerView reloadAllComponents]; + [pickerView selectRow:[alignmentOptionsList indexOfObject:alignmentLabel.text] inComponent:0 animated:NO]; + [self showPicker:pickerView]; + break; + } + case 4: + { + pickerView.tag = TAG_PICKER_POSITIONING; + [pickerView reloadAllComponents]; + [pickerView selectRow:[positioningOptionsList indexOfObject:positioningLabel.text] inComponent:0 animated:NO]; + [self showPicker:pickerView]; + break; + } + + default: + break; + } + break; + } + [aTableView deselectRowAtIndexPath:[tableView indexPathForSelectedRow] animated:YES]; +} + +#pragma mark - +#pragma mark UIPickerViewDataSource + +- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { + return 1; +} + +- (NSInteger)pickerView:(UIPickerView *)aPickerView numberOfRowsInComponent:(NSInteger)component { + if (aPickerView.tag == TAG_PICKER_LINK_TO) { + return [linkToOptionsList count]; + } else if (aPickerView.tag == TAG_PICKER_ALIGNMENT) { + return [alignmentOptionsList count]; + } else if (aPickerView.tag == TAG_PICKER_POSITIONING) { + return [positioningOptionsList count]; + } + return 0; +} + +#pragma mark - +#pragma mark UIPickerViewDelegate + +- (NSString *)pickerView:(UIPickerView *)aPickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { + if (aPickerView.tag == TAG_PICKER_LINK_TO) { + return [linkToOptionsList objectAtIndex:row]; + } else if (aPickerView.tag == TAG_PICKER_ALIGNMENT) { + return [alignmentOptionsList objectAtIndex:row]; + } else if (aPickerView.tag == TAG_PICKER_POSITIONING) { + return [positioningOptionsList objectAtIndex:row]; + } + + return @""; +} + +- (void)pickerView:(UIPickerView *)aPickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:media, @"media", mediaSettings, @"mediaSettings", nil]; + if (aPickerView.tag == TAG_PICKER_POSITIONING) { + positioningLabel.text = [positioningOptionsList objectAtIndex:row]; + if (row == 1) { + [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaAbove" object:self userInfo:userInfo]; + } else if (row == 2) { + [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:self userInfo:userInfo]; + } + } else { + if (aPickerView.tag == TAG_PICKER_LINK_TO) { + linkToLabel.text = [linkToOptionsList objectAtIndex:row]; + if (row == 0) { + mediaSettings.linkHref = @""; + } else if (row == 1){ + mediaSettings.linkHref = media.remoteURL; + } // else leave the linkHref alone + } else if (aPickerView.tag == TAG_PICKER_ALIGNMENT) { + alignmentLabel.text = [alignmentOptionsList objectAtIndex:row]; + if (row == 0) { + mediaSettings.alignment = @"alignnone"; + } else if (row == 1) { + mediaSettings.alignment = @"alignleft"; + } else if (row == 2) { + mediaSettings.alignment = @"aligncenter"; + } else if (row == 3) { + mediaSettings.alignment = @"alignright"; + } + } + [[NSNotificationCenter defaultCenter] + postNotificationName:@"UpdateMedia" + object:self + userInfo:userInfo]; + } + + [tableView reloadData]; +} + +#pragma mark - +#pragma mark Pickers and keyboard animations + +- (void)showPicker:(UIView *)picker { + if (isShowingKeyboard) { + [captionTextField resignFirstResponder]; + } + + if (IS_IPAD) { + UIViewController *fakeController = [[UIViewController alloc] init]; + fakeController.contentSizeForViewInPopover = CGSizeMake(320.0f, 216.0f); + + [fakeController.view addSubview:picker]; + popover = [[UIPopoverController alloc] initWithContentViewController:fakeController]; + if ([popover respondsToSelector:@selector(popoverBackgroundViewClass)]) { + popover.popoverBackgroundViewClass = [WPPopoverBackgroundView class]; + } + + CGRect popoverRect; + if (picker.tag == TAG_PICKER_LINK_TO) + popoverRect = [self.view convertRect:linkToLabel.frame fromView:[linkToLabel superview]]; + else if (picker.tag == TAG_PICKER_ALIGNMENT) + popoverRect = [self.view convertRect:alignmentLabel.frame fromView:[alignmentLabel superview]]; + else if (picker.tag == TAG_PICKER_POSITIONING) + popoverRect = [self.view convertRect:positioningLabel.frame fromView:[positioningLabel superview]]; + + popoverRect.size.width = 100.0f; + [popover presentPopoverFromRect:popoverRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; + + } else { + + CGFloat width = self.view.frame.size.width; + CGFloat height = 0.0; + + // Refactor this class to not use UIActionSheets for display. See trac #1509. + // Shoehorning a UIPicker inside a UIActionSheet is just madness. + // For now, hardcoding height values for the iPhone so we don't get + // a funky gap at the bottom of the screen on the iPhone 5. + if(self.view.frame.size.height <= 416.0f) { + height = 490.0f; + } else { + height = 500.0f; + } + if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){ + height = 460.0f; // Show most of the actionsheet but keep the top of the view visible. + } + + UIView *pickerWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, width, 260.0f)]; // 216 + 44 (height of the picker and the "tooblar") + pickerWrapperView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; + [pickerWrapperView addSubview:picker]; + + CGRect pickerFrame = picker.frame; + pickerFrame.size.width = width; + picker.frame = pickerFrame; + + actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:nil cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil]; + [actionSheet setActionSheetStyle:UIActionSheetStyleAutomatic]; + [actionSheet setBounds:CGRectMake(0.0f, 0.0f, width, height)]; + + [actionSheet addSubview:pickerWrapperView]; + + UISegmentedControl *closeButton = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:NSLocalizedString(@"Done", @"Default main action button for closing/finishing a work flow in the app (used in Comments>Edit, Comment edits and replies, post editor body text, etc, to dismiss keyboard).")]]; + closeButton.momentary = YES; + CGFloat x = self.view.frame.size.width - 60.0f; + closeButton.frame = CGRectMake(x, 7.0f, 50.0f, 30.0f); + closeButton.segmentedControlStyle = UISegmentedControlStyleBar; + if ([closeButton respondsToSelector:@selector(setTintColor:)]) { + closeButton.tintColor = [UIColor blackColor]; + } + [closeButton addTarget:self action:@selector(hidePicker) forControlEvents:UIControlEventValueChanged]; + closeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + [pickerWrapperView addSubview:closeButton]; + + UISegmentedControl *publishNowButton = nil; + + if ([[UISegmentedControl class] respondsToSelector:@selector(appearance)]) { + // Since we're requiring a black tint we do not want to use the custom text colors. + NSDictionary *titleTextAttributesForStateNormal = [NSDictionary dictionaryWithObjectsAndKeys: + [UIColor whiteColor], + UITextAttributeTextColor, + [UIColor darkGrayColor], + UITextAttributeTextShadowColor, + [NSValue valueWithUIOffset:UIOffsetMake(0, 1)], + UITextAttributeTextShadowOffset, + nil]; + + // The UISegmentControl does not show a pressed state for its button so (for now) use the same + // state for normal and highlighted. + // It would be nice to refactor this to use a toolbar and buttons instead of a segmented control to get the + // correct look and feel. + [closeButton setTitleTextAttributes:titleTextAttributesForStateNormal forState:UIControlStateNormal]; + [closeButton setTitleTextAttributes:titleTextAttributesForStateNormal forState:UIControlStateHighlighted]; + + if (publishNowButton) { + [publishNowButton setTitleTextAttributes:titleTextAttributesForStateNormal forState:UIControlStateNormal]; + [publishNowButton setTitleTextAttributes:titleTextAttributesForStateNormal forState:UIControlStateHighlighted]; + } + } + + [actionSheet showInView:self.view]; + [actionSheet setBounds:CGRectMake(0.0f, 0.0f, width, height)]; // Update the bounds again now that its in the view else it won't draw correctly. + } +} + +- (void)hidePicker { + [actionSheet dismissWithClickedButtonIndex:0 animated:YES]; + actionSheet = nil; +} + +- (void)keyboardWillShow:(NSNotification *)keyboardInfo { + if(IS_IPAD == NO) { + // on iphone it's possible we need to scroll the tableview when the keyboard is shown + if (isShowingKeyboard) { + return; + } + + NSDictionary* userInfo = [keyboardInfo userInfo]; + + // get the size of the keyboard + CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; + + // resize the tableview + CGRect viewFrame = tableView.frame; + viewFrame.size.height -= keyboardSize.height; + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationBeginsFromCurrentState:YES]; + [UIView setAnimationDuration:0.3f]; + [tableView setFrame:viewFrame]; + + // scroll the view if the caption textfield is not visible + if (tableView.contentOffset.y <= 100) { + tableView.contentOffset = CGPointMake(0, 100); + } + [UIView commitAnimations]; + } + isShowingKeyboard = YES; +} + +- (void)keyboardWillHide:(NSNotification *)keyboardInfo { + if(IS_IPAD == NO) { + NSDictionary* userInfo = [keyboardInfo userInfo]; + + // get the size of the keyboard + CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; + + // resize the tableview + CGRect viewFrame = tableView.frame; + viewFrame.size.height += keyboardSize.height; + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationBeginsFromCurrentState:YES]; + [UIView setAnimationDuration:0.3f]; + [tableView setFrame:viewFrame]; + [UIView commitAnimations]; + } + + isShowingKeyboard = NO; +} + +#pragma mark - +#pragma mark Rotation Methods + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return [super shouldAutorotateToInterfaceOrientation:interfaceOrientation]; +} + +- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { + [self reloadData]; +} + +#pragma mark - +#pragma mark Slider Methods +-(IBAction) sliderChanged:(id) sender{ + UISlider *slider = (UISlider *) sender; + // step by 10s + float newStep = roundf((slider.value) / 10.0f); + slider.value = newStep * 10.0f; + if (slider.value + 10 > slider.maximumValue) { + slider.value = slider.maximumValue; + } + int width = (int)slider.value; + int height = width * [media.height intValue]/[media.width intValue]; + imageSizeLabel.text = [NSString stringWithFormat:@"%d x %d", width, height]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:media, @"media", mediaSettings, @"mediaSettings", nil]; + mediaSettings.customWidth = [NSNumber numberWithInt:width]; + mediaSettings.customHeight = [NSNumber numberWithInt:height]; + [[NSNotificationCenter defaultCenter] + postNotificationName:@"UpdateMedia" + object:self + userInfo:userInfo]; +} + +#pragma mark - +#pragma mark TextField Methods + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [captionTextField resignFirstResponder]; + return YES; +} + +- (void)textFieldDidBeginEditing:(UITextField *)textField { + captionTextField.placeholder = nil; +} + +- (IBAction)textFieldReturn:(id)sender { + [sender resignFirstResponder]; +} + +- (IBAction)textFieldFinishedEditing:(id)sender { + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:media, @"media", mediaSettings, @"mediaSettings", nil]; + mediaSettings.captionText = captionTextField.text; + [[NSNotificationCenter defaultCenter] + postNotificationName:@"UpdateMedia" + object:self + userInfo:userInfo]; +} + +- (IBAction)backgroundTouched:(id)sender { + [captionTextField resignFirstResponder]; +} + +#pragma mark - +#pragma mark UIActionSheet delegate + +- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { + switch (buttonIndex) { + case 0: + [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldRemoveMedia" object:media]; + [media remove]; + if(IS_IPAD == YES) + [self dismissModalViewControllerAnimated:YES]; + else + [self.navigationController popViewControllerAnimated:YES]; + break; + default: + break; + } +} + +#pragma mark - +#pragma mark Cancel button actions + +- (IBAction)cancelSelection:(id)sender { + if (IS_IPAD) + [self dismissModalViewControllerAnimated:YES]; +} + +#pragma mark - +#pragma mark UIAlertViewDelegate delegate +- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { + if (buttonIndex == 1) { + [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldRemoveMedia" object:media]; + [media remove]; + [self dismissModalViewControllerAnimated:YES]; + } +} + +@end diff --git a/WordPress/Classes/NotificationsCommentDetailViewController.m b/WordPress/Classes/NotificationsCommentDetailViewController.m index 640b20f7ad86..ae076a013316 100644 --- a/WordPress/Classes/NotificationsCommentDetailViewController.m +++ b/WordPress/Classes/NotificationsCommentDetailViewController.m @@ -160,7 +160,7 @@ - (void)displayNote { self.disclosureIndicator.hidden = NO; NSString *postTitle = [[self.post valueForKeyPath:@"title"] stringByDecodingXMLCharacters]; if (!postTitle || [postTitle isEqualToString:@""]) - postTitle = NSLocalizedString(@"Unitled Post", @"Used when a post has no title"); + postTitle = NSLocalizedString(@"Untitled Post", @"Used when a post has no title"); self.postBanner.titleLabel.text = postTitle; id authorAvatarURL = [self.post valueForKeyPath:@"author.avatar_URL"]; if ([authorAvatarURL isKindOfClass:[NSString class]]) { diff --git a/WordPress/Classes/PostMediaViewController.h b/WordPress/Classes/PostMediaViewController.h old mode 100644 new mode 100755 index a9538a2a8f27..7785f68d39c2 --- a/WordPress/Classes/PostMediaViewController.h +++ b/WordPress/Classes/PostMediaViewController.h @@ -15,6 +15,8 @@ #import "UIImage+Resize.h" #import "WordPressAppDelegate.h" #import "MediaObjectViewController.h" +#import "MediaSettingsViewController.h" +#import "MediaSettings.h" typedef void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *asset); typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error); diff --git a/WordPress/Classes/PostMediaViewController.m b/WordPress/Classes/PostMediaViewController.m old mode 100644 new mode 100755 index 7ef01d7ed34a..58f5405ed040 --- a/WordPress/Classes/PostMediaViewController.m +++ b/WordPress/Classes/PostMediaViewController.m @@ -231,7 +231,8 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath NSLog(@"Media deleted while uploading (%@)", media); return; } - [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:media]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:media, @"media", [[MediaSettings alloc] init], @"mediaSettings", nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:self userInfo:userInfo]; [media save]; } failure:nil]; } else if (media.remoteStatus == MediaRemoteStatusPushing) { @@ -239,9 +240,20 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath } else if (media.remoteStatus == MediaRemoteStatusProcessing) { // Do nothing. See trac #1508 } else { - MediaObjectViewController *mediaView = [[MediaObjectViewController alloc] initWithNibName:@"MediaObjectView" bundle:nil]; - [mediaView setMedia:media]; - + // for images use the new media settings view + UIViewController *mediaView; + if ([media.mediaType isEqualToString:@"image"]) { + MediaSettingsViewController *tempMediaView = [[MediaSettingsViewController alloc] initWithNibName:@"MediaSettingsViewController" bundle:nil]; + [tempMediaView setMedia:media]; + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:media.remoteURL content:postDetailViewController.apost.content]; + [tempMediaView setMediaSettings:mediaSettings]; + mediaView = tempMediaView; + } else { + MediaObjectViewController *tempMediaView = [[MediaObjectViewController alloc] initWithNibName:@"MediaObjectView" bundle:nil]; + [tempMediaView setMedia:media]; + mediaView = tempMediaView; + } + if(IS_IPAD == YES) { mediaView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; mediaView.modalPresentationStyle = UIModalPresentationFormSheet; @@ -1347,7 +1359,8 @@ - (void)useImage:(UIImage *)theImage { return; } if (!isPickingFeaturedImage) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:imageMedia]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:imageMedia, @"media", [[MediaSettings alloc] init], @"mediaSettings", nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:self userInfo:userInfo]; } else { @@ -1442,7 +1455,8 @@ - (void)useVideo:(NSString *)videoURL { NSLog(@"Media deleted while uploading (%@)", videoMedia); return; } - [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:videoMedia]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:videoMedia, @"media", [[MediaSettings alloc] init], @"mediaSettings", nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:self userInfo:userInfo]; [videoMedia save]; } failure:nil]; isAddingMedia = NO; @@ -1484,7 +1498,8 @@ - (void)mediaDidUploadSuccessfully:(NSNotification *)notification { NSLog(@"Media deleted while uploading (%@)", media); return; } - [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:media]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:media, @"media", [[MediaSettings alloc] init], @"mediaSettings", nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"ShouldInsertMediaBelow" object:self userInfo:userInfo]; [media save]; self.isAddingMedia = NO; } diff --git a/WordPress/Classes/UINavigationController+Rotation.m b/WordPress/Classes/UINavigationController+Rotation.m index dcf02b3510d0..bbbc8b1d939c 100644 --- a/WordPress/Classes/UINavigationController+Rotation.m +++ b/WordPress/Classes/UINavigationController+Rotation.m @@ -25,11 +25,20 @@ - (NSUInteger)mySupportedInterfaceOrientations { return UIInterfaceOrientationMaskAll; } +- (BOOL)myShouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { + NSUInteger mask = [self mySupportedInterfaceOrientations]; + NSUInteger orientation = 1 << toInterfaceOrientation; + return (mask & orientation) ? YES : NO; +} + (void)load { Method origMethod = class_getInstanceMethod(self, @selector(supportedInterfaceOrientations)); Method newMethod = class_getInstanceMethod(self, @selector(mySupportedInterfaceOrientations)); method_exchangeImplementations(origMethod, newMethod); + + origMethod = class_getInstanceMethod(self, @selector(shouldAutorotateToInterfaceOrientation:)); + newMethod = class_getInstanceMethod(self, @selector(myShouldAutorotateToInterfaceOrientation:)); + method_exchangeImplementations(origMethod, newMethod); } diff --git a/WordPress/Classes/WPFriendFinderViewController.m b/WordPress/Classes/WPFriendFinderViewController.m index 7c2c59ceccbe..60b6d58e9181 100644 --- a/WordPress/Classes/WPFriendFinderViewController.m +++ b/WordPress/Classes/WPFriendFinderViewController.m @@ -305,14 +305,18 @@ - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)butto [defaults setBool:YES forKey:kAccessedAddressBookPreference]; [defaults synchronize]; } - self.dismissBlock(buttonIndex); + if (self.dismissBlock) { + self.dismissBlock(buttonIndex); + } } // Called when we cancel a view (eg. the user clicks the Home button). This is not called when the user clicks the cancel button. // If not defined in the delegate, we simulate a click in the cancel button - (void)alertViewCancel:(UIAlertView *)alertView { - self.dismissBlock(-1); + if (self.dismissBlock) { + self.dismissBlock(-1); + } } diff --git a/WordPress/Classes/WPReaderViewController.m b/WordPress/Classes/WPReaderViewController.m index 46a196b30adc..676a48fb47d7 100644 --- a/WordPress/Classes/WPReaderViewController.m +++ b/WordPress/Classes/WPReaderViewController.m @@ -523,10 +523,6 @@ - (void)webViewDidFinishLoad:(UIWebView *)aWebView { - (BOOL) shouldDisplayfriendFinderNudgeView { - #ifdef DEBUG - return self.friendFinderNudgeView == nil; - #endif - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; return ![userDefaults boolForKey:WPReaderViewControllerDisplayedFriendFinder] && self.friendFinderNudgeView == nil; } diff --git a/WordPress/Classes/WPTableViewController.m b/WordPress/Classes/WPTableViewController.m index 32e373df38e4..4d8e446fb729 100644 --- a/WordPress/Classes/WPTableViewController.m +++ b/WordPress/Classes/WPTableViewController.m @@ -619,21 +619,27 @@ - (void)syncItemsWithUserInteraction:(BOOL)userInteraction { _isSyncing = NO; [self configureNoResultsView]; if (self.blog) { - if (error.code == 405) { - // Prompt to enable XML-RPC using the default message provided from the WordPress site. - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Couldn't sync", @"") - message:[error localizedDescription] - delegate:self - cancelButtonTitle:NSLocalizedString(@"Need Help?", @"") - otherButtonTitles:NSLocalizedString(@"Enable Now", @""), nil]; - - alertView.tag = 30; - [alertView show]; - - } else if (error.code == 403 && editSiteViewController == nil) { - [self promptForPassword]; - } else if (userInteraction) { - [WPError showAlertWithError:error title:NSLocalizedString(@"Couldn't sync", @"")]; + if ([error.domain isEqualToString:@"XMLRPC"]) { + if (error.code == 405) { + // Prompt to enable XML-RPC using the default message provided from the WordPress site. + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Couldn't sync", @"") + message:[error localizedDescription] + delegate:self + cancelButtonTitle:NSLocalizedString(@"Need Help?", @"") + otherButtonTitles:NSLocalizedString(@"Enable Now", @""), nil]; + + alertView.tag = 30; + [alertView show]; + + } else if (error.code == 403 && editSiteViewController == nil) { + [self promptForPassword]; + } else if (error.code == 425 && editSiteViewController == nil) { + [self promptForPasswordWithMessage:[error localizedDescription]]; + } else if (userInteraction) { + [WPError showAlertWithError:error title:NSLocalizedString(@"Couldn't sync", @"")]; + } + } else { + [WPError showAlertWithError:error]; } } else { // For non-blog tables (notifications), just show the error for now @@ -643,8 +649,15 @@ - (void)syncItemsWithUserInteraction:(BOOL)userInteraction { } - (void)promptForPassword { + [self promptForPasswordWithMessage:nil]; +} + +- (void)promptForPasswordWithMessage:(NSString *)message { + if (message == nil) { + message = NSLocalizedString(@"The username or password stored in the app may be out of date. Please re-enter your password in the settings and try again.", @""); + } UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Couldn't Connect", @"") - message:NSLocalizedString(@"The username or password stored in the app may be out of date. Please re-enter your password in the settings and try again.", @"") + message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:NSLocalizedString(@"OK", @""), nil]; diff --git a/WordPress/Resources/Images/button_red.png b/WordPress/Resources/Images/button_red.png new file mode 100755 index 000000000000..a77d5a276541 Binary files /dev/null and b/WordPress/Resources/Images/button_red.png differ diff --git a/WordPress/Resources/Images/button_red@2x.png b/WordPress/Resources/Images/button_red@2x.png new file mode 100755 index 000000000000..e90738d862df Binary files /dev/null and b/WordPress/Resources/Images/button_red@2x.png differ diff --git a/WordPress/Resources/MediaSettingsViewController.xib b/WordPress/Resources/MediaSettingsViewController.xib new file mode 100755 index 000000000000..f4214c9d033e --- /dev/null +++ b/WordPress/Resources/MediaSettingsViewController.xib @@ -0,0 +1,1087 @@ + + + + 784 + 12C3006 + 2840 + 1187.34 + 625.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 1926 + + + IBProxyObject + IBUIBarButtonItem + IBUIImageView + IBUILabel + IBUISlider + IBUITableView + IBUITableViewCell + IBUITextField + IBUIView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + + + + 274 + {320, 372} + + + + 3 + MCAwAA + + NO + YES + NO + IBCocoaTouchFramework + 1 + 1 + 0 + YES + 44 + 15 + 10 + + + {{0, 64}, {320, 372}} + + + + + 3 + MQA + + NO + + + NO + + + IBCocoaTouchFramework + + + + 292 + + + + 256 + + + + 292 + {{8, 11}, {57, 21}} + + + + NO + YES + NO + IBCocoaTouchFramework + Link to + + 1 + MCAwIDAAA + darkTextColor + + + 1 + 10 + + Helvetica-Bold + Helvetica + 2 + 17 + + + Helvetica-Bold + 17 + 16 + + + + + 290 + {{94, 11}, {206, 21}} + + + NO + YES + 7 + NO + IBCocoaTouchFramework + Media File + + + 1 + 10 + + 1 + 17 + + + Helvetica + 17 + 16 + + + + {320, 43} + + + + + NO + YES + 4 + YES + IBCocoaTouchFramework + + + {320, 44} + + + + + 1 + MSAxIDEAA + + IBCocoaTouchFramework + + + + + 292 + + + + 256 + + + + 293 + {{20, 12}, {280, 163}} + + + _NS:9 + 1 + NO + IBCocoaTouchFramework + + + {320, 187} + + + + _NS:11 + + NO + YES + 4 + YES + IBCocoaTouchFramework + + + {320, 188} + + + + _NS:9 + IBCocoaTouchFramework + + + + + 292 + + + + 256 + + + + 292 + {{8, 11}, {84, 21}} + + + + NO + YES + NO + IBCocoaTouchFramework + Alignment + + + 1 + 10 + + + + + + 290 + {{94, 11}, {206, 21}} + + + NO + YES + 7 + NO + IBCocoaTouchFramework + None + + + 1 + 10 + + + + + {320, 43} + + + + + NO + YES + 4 + YES + IBCocoaTouchFramework + + + {320, 44} + + + + + IBCocoaTouchFramework + + + + + 292 + + + + 256 + + + + 292 + {{8, 11}, {93, 21}} + + + + NO + YES + NO + IBCocoaTouchFramework + Positioning + + + 1 + 10 + + + + + + 290 + {{94, 11}, {206, 21}} + + + NO + YES + 7 + NO + IBCocoaTouchFramework + Above Content + + + 1 + 10 + + + + + {320, 43} + + + + + NO + YES + 4 + YES + IBCocoaTouchFramework + + + {320, 44} + + + + + IBCocoaTouchFramework + + + + + 292 + + + + 256 + + + + 292 + {{8, 11}, {64, 21}} + + + + NO + YES + NO + IBCocoaTouchFramework + Caption + + + 1 + 10 + + + + + + 290 + {{94, 7}, {206, 30}} + + + _NS:9 + NO + YES + IBCocoaTouchFramework + 0 + + 3 + + 3 + MAA + + 2 + + + YES + 17 + + IBCocoaTouchFramework + + + 1 + 14 + + + Helvetica + 14 + 16 + + + + {320, 43} + + + + + NO + YES + 4 + YES + IBCocoaTouchFramework + + + {320, 44} + + + + + IBCocoaTouchFramework + + + + + 292 + + + + 256 + + + + 292 + {{8, 11}, {48, 21}} + + + + NO + YES + NO + IBCocoaTouchFramework + Width + + + 1 + 10 + + + + + + 290 + {{94, 11}, {206, 23}} + + + + _NS:9 + NO + IBCocoaTouchFramework + 0 + 0 + 500 + 10 + 1000 + + + + 292 + {{96, 41}, {206, 21}} + + + NO + YES + 7 + NO + IBCocoaTouchFramework + Image Size + + + 1 + 10 + + + + + {320, 78} + + + + + NO + YES + 4 + YES + IBCocoaTouchFramework + + + {320, 79} + + + + + IBCocoaTouchFramework + + + + + Item + IBCocoaTouchFramework + 1 + + + + + + + view + + + + 128 + + + + tableView + + + + 254 + + + + linkToLabel + + + + 276 + + + + linkToTitleLabel + + + + 277 + + + + linkToTableViewCell + + + + 278 + + + + captionTableViewCell + + + + 294 + + + + captionTitleLabel + + + + 295 + + + + captionTextField + + + + 296 + + + + widthTitleLabel + + + + 298 + + + + widthTableViewCell + + + + 299 + + + + widthSlider + + + + 300 + + + + positioningTableViewCell + + + + 301 + + + + positioningTitleLabel + + + + 302 + + + + positioningLabel + + + + 303 + + + + alignmentTableViewCell + + + + 304 + + + + alignmentTitleLabel + + + + 305 + + + + alignmentLabel + + + + 306 + + + + thumbnail + + + + 307 + + + + imageSizeLabel + + + + 310 + + + + mediaTableViewCell + + + + 313 + + + + cancelButton + + + + 316 + + + + backgroundTouched: + + + 1 + + 312 + + + + dataSource + + + + 8 + + + + delegate + + + + 9 + + + + textFieldReturn: + + + 20 + + 311 + + + + textFieldFinishedEditing: + + + 19 + + 314 + + + + sliderChanged: + + + 13 + + 309 + + + + cancelSelection: + + + + 317 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 2 + + + + + + + + 6 + + + + + + 223 + + + + + + + Link to + + + 226 + + + + + 227 + + + + + 263 + + + + + + Thumbnail + + + 266 + + + + + 279 + + + + + + + Alignment + + + 280 + + + + + 281 + + + + + 282 + + + + + + + Positioning + + + 284 + + + + + 283 + + + + + 285 + + + + + + + Caption + + + 287 + + + + + 288 + + + + + 289 + + + + + + + + Width + + + 291 + + + + + 292 + + + + + 308 + + + + + 315 + + + + + + + MediaSettingsViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIControl + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 317 + + + + + MediaSettingsViewController + UIViewController + + cancelSelection: + id + + + cancelSelection: + + cancelSelection: + id + + + + UILabel + UITableViewCell + UILabel + UIBarButtonItem + UITableViewCell + UITextField + UILabel + UILabel + UILabel + UITableViewCell + UILabel + UITableViewCell + UILabel + UITableViewCell + UILabel + UITableView + UIImageView + UISlider + UITableViewCell + UILabel + + + + alignmentLabel + UILabel + + + alignmentTableViewCell + UITableViewCell + + + alignmentTitleLabel + UILabel + + + cancelButton + UIBarButtonItem + + + captionTableViewCell + UITableViewCell + + + captionTextField + UITextField + + + captionTitleLabel + UILabel + + + imageSizeLabel + UILabel + + + linkToLabel + UILabel + + + linkToTableViewCell + UITableViewCell + + + linkToTitleLabel + UILabel + + + mediaTableViewCell + UITableViewCell + + + positioningLabel + UILabel + + + positioningTableViewCell + UITableViewCell + + + positioningTitleLabel + UILabel + + + tableView + UITableView + + + thumbnail + UIImageView + + + widthSlider + UISlider + + + widthTableViewCell + UITableViewCell + + + widthTitleLabel + UILabel + + + + IBProjectSource + ./Classes/MediaSettingsViewController.h + + + + + 0 + IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + YES + 3 + 1926 + + diff --git a/WordPress/Resources/de.lproj/Localizable.strings b/WordPress/Resources/de.lproj/Localizable.strings index 18bfd29d9ca6..fd9133aa6e82 100644 Binary files a/WordPress/Resources/de.lproj/Localizable.strings and b/WordPress/Resources/de.lproj/Localizable.strings differ diff --git a/WordPress/Resources/en.lproj/Localizable.strings b/WordPress/Resources/en.lproj/Localizable.strings index 09a875790802..4db406b4ca71 100644 Binary files a/WordPress/Resources/en.lproj/Localizable.strings and b/WordPress/Resources/en.lproj/Localizable.strings differ diff --git a/WordPress/Resources/es.lproj/Localizable.strings b/WordPress/Resources/es.lproj/Localizable.strings index a3d69c9febee..684eaa57308f 100644 Binary files a/WordPress/Resources/es.lproj/Localizable.strings and b/WordPress/Resources/es.lproj/Localizable.strings differ diff --git a/WordPress/Resources/fr.lproj/Localizable.strings b/WordPress/Resources/fr.lproj/Localizable.strings index 8fb3a5bf6afb..baf680b01c78 100644 Binary files a/WordPress/Resources/fr.lproj/Localizable.strings and b/WordPress/Resources/fr.lproj/Localizable.strings differ diff --git a/WordPress/Resources/he.lproj/Localizable.strings b/WordPress/Resources/he.lproj/Localizable.strings index 5ebe057dfcf1..18fed8d83554 100644 Binary files a/WordPress/Resources/he.lproj/Localizable.strings and b/WordPress/Resources/he.lproj/Localizable.strings differ diff --git a/WordPress/Resources/hr.lproj/Localizable.strings b/WordPress/Resources/hr.lproj/Localizable.strings index 5e240c1d6043..4914bec10fb7 100644 Binary files a/WordPress/Resources/hr.lproj/Localizable.strings and b/WordPress/Resources/hr.lproj/Localizable.strings differ diff --git a/WordPress/Resources/hu.lproj/Localizable.strings b/WordPress/Resources/hu.lproj/Localizable.strings index f9b1a953b3aa..ddd7e7f694bc 100644 Binary files a/WordPress/Resources/hu.lproj/Localizable.strings and b/WordPress/Resources/hu.lproj/Localizable.strings differ diff --git a/WordPress/Resources/id.lproj/Localizable.strings b/WordPress/Resources/id.lproj/Localizable.strings index e3690f1b7aec..3f30d5c6af29 100644 Binary files a/WordPress/Resources/id.lproj/Localizable.strings and b/WordPress/Resources/id.lproj/Localizable.strings differ diff --git a/WordPress/Resources/it.lproj/Localizable.strings b/WordPress/Resources/it.lproj/Localizable.strings index d2331e86acbe..4562575faab1 100644 Binary files a/WordPress/Resources/it.lproj/Localizable.strings and b/WordPress/Resources/it.lproj/Localizable.strings differ diff --git a/WordPress/Resources/ja.lproj/Localizable.strings b/WordPress/Resources/ja.lproj/Localizable.strings index 34f5ed031201..b48762d58c7b 100644 Binary files a/WordPress/Resources/ja.lproj/Localizable.strings and b/WordPress/Resources/ja.lproj/Localizable.strings differ diff --git a/WordPress/Resources/nb.lproj/Localizable.strings b/WordPress/Resources/nb.lproj/Localizable.strings index 494305a64f21..adb27c2900af 100644 Binary files a/WordPress/Resources/nb.lproj/Localizable.strings and b/WordPress/Resources/nb.lproj/Localizable.strings differ diff --git a/WordPress/Resources/nl.lproj/Localizable.strings b/WordPress/Resources/nl.lproj/Localizable.strings index bd45bc6ed0c1..7c89136f4b78 100644 Binary files a/WordPress/Resources/nl.lproj/Localizable.strings and b/WordPress/Resources/nl.lproj/Localizable.strings differ diff --git a/WordPress/Resources/pl.lproj/Localizable.strings b/WordPress/Resources/pl.lproj/Localizable.strings index 4cfb46c000f2..048321f1c8c9 100644 Binary files a/WordPress/Resources/pl.lproj/Localizable.strings and b/WordPress/Resources/pl.lproj/Localizable.strings differ diff --git a/WordPress/Resources/pt.lproj/Localizable.strings b/WordPress/Resources/pt.lproj/Localizable.strings index cf131ca1eae0..81e4418e67da 100644 Binary files a/WordPress/Resources/pt.lproj/Localizable.strings and b/WordPress/Resources/pt.lproj/Localizable.strings differ diff --git a/WordPress/Resources/sv.lproj/Localizable.strings b/WordPress/Resources/sv.lproj/Localizable.strings index 115c9d9cbdea..35d1ec55f578 100644 Binary files a/WordPress/Resources/sv.lproj/Localizable.strings and b/WordPress/Resources/sv.lproj/Localizable.strings differ diff --git a/WordPress/Resources/tr.lproj/Localizable.strings b/WordPress/Resources/tr.lproj/Localizable.strings index fb29c8ae1677..fda9db6ef619 100644 Binary files a/WordPress/Resources/tr.lproj/Localizable.strings and b/WordPress/Resources/tr.lproj/Localizable.strings differ diff --git a/WordPress/Resources/zh-Hans.lproj/Localizable.strings b/WordPress/Resources/zh-Hans.lproj/Localizable.strings index 422aa9af8db6..d625d0d05adf 100644 Binary files a/WordPress/Resources/zh-Hans.lproj/Localizable.strings and b/WordPress/Resources/zh-Hans.lproj/Localizable.strings differ diff --git a/WordPress/Resources/zh-Hant.lproj/Localizable.strings b/WordPress/Resources/zh-Hant.lproj/Localizable.strings index d35c6fb2bb5d..7bb77b5595e7 100644 Binary files a/WordPress/Resources/zh-Hant.lproj/Localizable.strings and b/WordPress/Resources/zh-Hant.lproj/Localizable.strings differ diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 158db8a7bbbd..e9e91c55f191 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -7,6 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + 00C0D2581718AB1C00E2B95D /* button_red.png in Resources */ = {isa = PBXBuildFile; fileRef = 00C0D2561718AB1C00E2B95D /* button_red.png */; }; + 00C0D2591718AB1C00E2B95D /* button_red@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 00C0D2571718AB1C00E2B95D /* button_red@2x.png */; }; + 00C0D25B1718AB4B00E2B95D /* MediaSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 00C0D25A1718AB4B00E2B95D /* MediaSettingsViewController.xib */; }; + 00C0D25E1718AB6300E2B95D /* MediaSettingsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C0D25D1718AB6300E2B95D /* MediaSettingsTests.m */; }; + 00C0D2611718AB9C00E2B95D /* MediaSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C0D2601718AB9C00E2B95D /* MediaSettingsViewController.m */; }; + 00C0D2641718ABCC00E2B95D /* MediaSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C0D2631718ABCC00E2B95D /* MediaSettings.m */; }; + 00C0D2651718ABCC00E2B95D /* MediaSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C0D2631718ABCC00E2B95D /* MediaSettings.m */; }; 00F2E3F8166EEF9800D0527C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 834CE7371256D0F60046A4A3 /* CoreGraphics.framework */; }; 00F2E3FA166EEFBE00D0527C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E10B3653158F2D4500419A93 /* UIKit.framework */; }; 00F2E3FB166EEFE100D0527C /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E10B3651158F2D3F00419A93 /* QuartzCore.framework */; }; @@ -721,6 +728,15 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 00C0D2561718AB1C00E2B95D /* button_red.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = button_red.png; path = Resources/Images/button_red.png; sourceTree = ""; }; + 00C0D2571718AB1C00E2B95D /* button_red@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "button_red@2x.png"; path = "Resources/Images/button_red@2x.png"; sourceTree = ""; }; + 00C0D25A1718AB4B00E2B95D /* MediaSettingsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = MediaSettingsViewController.xib; path = Resources/MediaSettingsViewController.xib; sourceTree = ""; }; + 00C0D25C1718AB6300E2B95D /* MediaSettingsTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaSettingsTests.h; sourceTree = ""; }; + 00C0D25D1718AB6300E2B95D /* MediaSettingsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MediaSettingsTests.m; sourceTree = ""; }; + 00C0D25F1718AB9C00E2B95D /* MediaSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaSettingsViewController.h; sourceTree = ""; }; + 00C0D2601718AB9C00E2B95D /* MediaSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MediaSettingsViewController.m; sourceTree = ""; }; + 00C0D2621718ABCC00E2B95D /* MediaSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaSettings.h; sourceTree = ""; }; + 00C0D2631718ABCC00E2B95D /* MediaSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MediaSettings.m; sourceTree = ""; }; 031663050FFB151A0045D052 /* PostTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostTableViewCell.h; sourceTree = ""; }; 031663060FFB151A0045D052 /* PostTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PostTableViewCell.m; sourceTree = ""; }; 031666AC0FFC3E130045D052 /* CommentTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommentTableViewCell.h; sourceTree = ""; }; @@ -2262,6 +2278,7 @@ 83290399120CF517000A965A /* Media */ = { isa = PBXGroup; children = ( + 00C0D25A1718AB4B00E2B95D /* MediaSettingsViewController.xib */, 83CAD4201235F9F4003DFA20 /* MediaObjectView.xib */, 8383178712270E980047B476 /* PostMediaViewController.xib */, ); @@ -2282,6 +2299,10 @@ 83CAD4191235F823003DFA20 /* Media */ = { isa = PBXGroup; children = ( + 00C0D2621718ABCC00E2B95D /* MediaSettings.h */, + 00C0D2631718ABCC00E2B95D /* MediaSettings.m */, + 00C0D25F1718AB9C00E2B95D /* MediaSettingsViewController.h */, + 00C0D2601718AB9C00E2B95D /* MediaSettingsViewController.m */, 8383178512270E980047B476 /* PostMediaViewController.h */, 8383178612270E980047B476 /* PostMediaViewController.m */, 83CAD41A1235F854003DFA20 /* MediaObjectViewController.h */, @@ -2387,6 +2408,8 @@ AC512F790E20D25E00B7DA32 /* Images */ = { isa = PBXGroup; children = ( + 00C0D2561718AB1C00E2B95D /* button_red.png */, + 00C0D2571718AB1C00E2B95D /* button_red@2x.png */, E1D0D82D16D3D2BA00E33F4C /* NNInstapaperActivity@2x~ipad.png */, E1D0D82E16D3D2BA00E33F4C /* NNInstapaperActivity@2x~iphone.png */, E1D0D82F16D3D2BA00E33F4C /* NNInstapaperActivity~ipad.png */, @@ -2920,6 +2943,8 @@ E183EC9F16B2164900C2EB11 /* EditPostViewControllerTest.m */, E150520A16CAC5C400D3DDDC /* BlogJetpackTest.h */, E150520B16CAC5C400D3DDDC /* BlogJetpackTest.m */, + 00C0D25C1718AB6300E2B95D /* MediaSettingsTests.h */, + 00C0D25D1718AB6300E2B95D /* MediaSettingsTests.m */, ); path = WordPressTest; sourceTree = ""; @@ -3617,6 +3642,9 @@ E12A08E416C9706200E3DDF6 /* clouds_header@2x.png in Resources */, E15051CE16CA8E4900D3DDDC /* logo_jetpack.png in Resources */, E15051CF16CA8E4900D3DDDC /* logo_jetpack@2x.png in Resources */, + 00C0D2581718AB1C00E2B95D /* button_red.png in Resources */, + 00C0D2591718AB1C00E2B95D /* button_red@2x.png in Resources */, + 00C0D25B1718AB4B00E2B95D /* MediaSettingsViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3832,6 +3860,8 @@ E1D0D82B16D3D19200E33F4C /* PocketAPIOperation.m in Sources */, E1D0D84716D3D2EA00E33F4C /* PocketActivity.m in Sources */, E15051CB16CA5DDB00D3DDDC /* Blog+Jetpack.m in Sources */, + 00C0D2611718AB9C00E2B95D /* MediaSettingsViewController.m in Sources */, + 00C0D2641718ABCC00E2B95D /* MediaSettings.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3847,6 +3877,8 @@ E150520F16CAC75A00D3DDDC /* CoreDataTestHelper.m in Sources */, E131CB5E16CAD659004B0314 /* AsyncTestHelper.m in Sources */, E15618FD16DB8677006532C4 /* UIKitTestHelper.m in Sources */, + 00C0D25E1718AB6300E2B95D /* MediaSettingsTests.m in Sources */, + 00C0D2651718ABCC00E2B95D /* MediaSettings.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/WordPress/WordPressTest/MediaSettingsTests.h b/WordPress/WordPressTest/MediaSettingsTests.h new file mode 100644 index 000000000000..e1154f507580 --- /dev/null +++ b/WordPress/WordPressTest/MediaSettingsTests.h @@ -0,0 +1,14 @@ +// +// MediaSettingsTests.h +// WordPress +// +// Created by Jeffrey Vanneste on 2013-01-12. +// Copyright (c) 2013 WordPress. All rights reserved. +// + +#import +#import "MediaSettings.h" + +@interface MediaSettingsTests : SenTestCase + +@end diff --git a/WordPress/WordPressTest/MediaSettingsTests.m b/WordPress/WordPressTest/MediaSettingsTests.m new file mode 100644 index 000000000000..e9355fea49ff --- /dev/null +++ b/WordPress/WordPressTest/MediaSettingsTests.m @@ -0,0 +1,251 @@ +// +// MediaSettingsTests.m +// WordPress +// +// Created by Jeffrey Vanneste on 2013-01-12. +// Copyright (c) 2013 WordPress. All rights reserved. +// + +#import "MediaSettingsTests.h" + +@implementation MediaSettingsTests + +- (void)setUp +{ + [super setUp]; + + // Set-up code here. +} + +- (void)tearDown +{ + // Tear-down code here. + + [super tearDown]; +} + +- (void)testParseSettingsWhenUrlNotFound { + // setup + NSString *content = @"This image links to the image we want to match but we shouldn't match it since the URL is not in the img src "; + + // test + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurlnotfound.com/image.jpg" content:content]; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertNotNil(mediaSettings, @"mediaSettings should not be nil"); + STAssertNil(mediaSettings.parsedHtml, @"parsedHtml will be nil since no matching HTML was found"); +} + +- (void)testParseSettingsUrlMatchInContentAndImage { + // setup + NSString *content = @"This image links to the image we want to match but we shouldn't match it since the URL is not in the img src "; + NSString *parsedHtml = @""; + + // test + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image.jpg" content:content]; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(parsedHtml, mediaSettings.parsedHtml, @"parsedHtml"); +} + +- (void)testParseSettingsForMultipleImages { + // setup + NSString *content = @"

Previous content

\n\n and some post content
end
"; + NSString *parsedHtml = @""; + + // test + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl2.com/image2.jpg" content:content]; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(parsedHtml, mediaSettings.parsedHtml, @"parsedHtml"); +} + +- (void)testParseSettingsForSingleImageWithoutLinkAndNoAttributes { + // setup + NSString *content = @"

Previous content

\n\n and some post content
end
"; + NSString *parsedHtml = @""; + + // test + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image.jpg" content:content]; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(parsedHtml, mediaSettings.parsedHtml, @"parsedHtml"); +} + +- (void)testParseSettingsForSingleImageWithoutLink { + // setup + NSString *content = @"

Previous content

\n\n and some post content
end
"; + MediaSettings *expectedMediaSettings = [[MediaSettings alloc] init]; + NSString *parsedHtml = @""; + expectedMediaSettings.alignment = @"alignnone"; + expectedMediaSettings.customHeight = [NSNumber numberWithInt:405]; + expectedMediaSettings.customWidth = [NSNumber numberWithInt:540]; + + // test + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image.jpg" content:content]; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(parsedHtml, mediaSettings.parsedHtml, @"parsedHtml"); + STAssertEqualObjects(expectedMediaSettings.alignment, mediaSettings.alignment, @"alignment"); + STAssertEqualObjects(expectedMediaSettings.customHeight, mediaSettings.customHeight, @"customHeight"); + STAssertEqualObjects(expectedMediaSettings.customWidth, mediaSettings.customWidth, @"customWidth"); +} + +- (void)testParseSettingsForSingleImageWithLink { + // setup + NSString *content = @"

Previous content

\n\n and some post content
end
"; + MediaSettings *expectedMediaSettings = [[MediaSettings alloc] init]; + NSString *parsedHtml = @""; + expectedMediaSettings.alignment = @"alignnone"; + expectedMediaSettings.customHeight = [NSNumber numberWithInt:405]; + expectedMediaSettings.customWidth = [NSNumber numberWithInt:540]; + expectedMediaSettings.linkHref = @"http://www.someurl.com/image.jpg"; + + // test + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image.jpg" content:content]; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(parsedHtml, mediaSettings.parsedHtml, @"parsedHtml"); + STAssertEqualObjects(expectedMediaSettings.alignment, mediaSettings.alignment, @"alignment"); + STAssertEqualObjects(expectedMediaSettings.customHeight, mediaSettings.customHeight, @"customHeight"); + STAssertEqualObjects(expectedMediaSettings.customWidth, mediaSettings.customWidth, @"customWidth"); + STAssertEqualObjects(expectedMediaSettings.linkHref, mediaSettings.linkHref, @"linkHref"); +} + +- (void)testParseSettingsForMultipleImageWithCaption { + // setup + NSString *content = @"

Previous content

\n\n[caption id=\"captionid2\" align=\"aligncenter\" width=\"302\"]\"\" asdfsdf[/caption] and some post content
end
[caption id=\"caption_id1\" align=\"aligncenter\" width=\"302\"]\"\" asdfsdf[/caption]"; + MediaSettings *expectedMediaSettings = [[MediaSettings alloc] init]; + NSString *parsedHtml = @"[caption id=\"caption_id1\" align=\"aligncenter\" width=\"302\"]\"\" asdfsdf[/caption]"; + expectedMediaSettings.customHeight = [NSNumber numberWithInt:227]; + expectedMediaSettings.customWidth = [NSNumber numberWithInt:302]; + expectedMediaSettings.linkHref = @"http://www.someurl.com/image1.jpg"; + expectedMediaSettings.captionText = @" asdfsdf"; + expectedMediaSettings.alignment = @"aligncenter"; + + // test + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image1.jpg" content:content]; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(parsedHtml, mediaSettings.parsedHtml, @"parsedHtml"); + STAssertEqualObjects(expectedMediaSettings.alignment, mediaSettings.alignment, @"alignment"); + STAssertEqualObjects(expectedMediaSettings.customHeight, mediaSettings.customHeight, @"customHeight"); + STAssertEqualObjects(expectedMediaSettings.customWidth, mediaSettings.customWidth, @"customWidth"); + STAssertEqualObjects(expectedMediaSettings.linkHref, mediaSettings.linkHref, @"linkHref"); + STAssertEqualObjects(expectedMediaSettings.captionText, mediaSettings.captionText, @"captionText"); +} + +- (void)testParseSettingsForMultipleImageWithCaptionAndNoLink { + // setup + NSString *content = @"

Previous content

\n\n[caption id=\"captionid2\" align=\"aligncenter\" width=\"302\"]\"\" asdfsdf[/caption] and some post content
end
[caption id=\"caption_id1\" align=\"aligncenter\" width=\"302\"]\"\"asdfsdf[/caption]"; + MediaSettings *expectedMediaSettings = [[MediaSettings alloc] init]; + NSString *parsedHtml = @"[caption id=\"caption_id1\" align=\"aligncenter\" width=\"302\"]\"\"asdfsdf[/caption]"; + expectedMediaSettings.customHeight = [NSNumber numberWithInt:227]; + expectedMediaSettings.customWidth = [NSNumber numberWithInt:302]; + expectedMediaSettings.captionText = @"asdfsdf"; + expectedMediaSettings.alignment = @"aligncenter"; + + // test + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image1.jpg" content:content]; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(parsedHtml, mediaSettings.parsedHtml, @"parsedHtml"); + STAssertEqualObjects(expectedMediaSettings.customHeight, mediaSettings.customHeight, @"customHeight"); + STAssertEqualObjects(expectedMediaSettings.customWidth, mediaSettings.customWidth, @"customWidth"); + STAssertEqualObjects(expectedMediaSettings.captionText, mediaSettings.captionText, @"captionText"); + STAssertEqualObjects(expectedMediaSettings.alignment, mediaSettings.alignment, @"alignment"); +} + + +- (void)testParseSettingsAndUpdateWithoutLosingAttributes { + // setup + NSString *content = @"

Previous content

\n\n[caption id=\"captionid2\" align=\"aligncenter\" width=\"302\"]\"\" asdfsdf[/caption] and some post content
end
[caption id=\"caption_id1\" align=\"aligncenter\" width=\"302\"]\"\"asdfsdf[/caption]"; + NSString *expectedMediaHtml = @"[caption id=\"captionid2\" align=\"alignleft\" width=\"302\"]\"\" asdfsdf[/caption]"; + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image2.jpg" content:content]; + + // test + mediaSettings.alignment = @"alignleft"; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(expectedMediaHtml, [mediaSettings html], @"html"); +} + +- (void)testUpdateWithAddingCaption { + // setup + NSString *content = @"

paragraph text

"; + NSString *expectedMediaHtml = @"[caption align=\"alignnone\"]brand new caption text[/caption]"; + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image1.jpg" content:content]; + + // test + mediaSettings.captionText = @"brand new caption text"; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(expectedMediaHtml, [mediaSettings html], @"html"); +} + +- (void)testUpdateWithAddingCaptionAndAlignmentRemovedFromImage { + // setup + NSString *content = @"

paragraph text

"; + NSString *expectedMediaHtml = @"[caption align=\"alignleft\"]brand new caption text[/caption]"; + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image1.jpg" content:content]; + + // test + mediaSettings.captionText = @"brand new caption text"; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(expectedMediaHtml, [mediaSettings html], @"html"); +} + +- (void)testUpdateWithRemovingCaptionAndAlignmentMovedToImage { + // setup + NSString *content = @"

paragraph text

[caption id=\"123\" align=' aligncenter ']existing caption[/caption]"; + NSString *expectedMediaHtml = @""; + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image1.jpg" content:content]; + + // test + mediaSettings.captionText = @""; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(expectedMediaHtml, [mediaSettings html], @"html"); +} + +- (void)testUpdateWithAddingLink { + // setup + NSString *content = @"

paragraph text

[caption id=\"123\" align=' aligncenter ']existing caption[/caption]"; + NSString *expectedMediaHtml = @"[caption id=\"123\" align=\"aligncenter\"]existing caption[/caption]"; + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image1.jpg" content:content]; + + // test + mediaSettings.linkHref = @"http://www.google.com"; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(expectedMediaHtml, [mediaSettings html], @"html"); +} + +- (void)testUpdateWithRemovingLink { + // setup + NSString *content = @"

paragraph text

[caption id=\"123\" align=' aligncenter ']existing caption[/caption]"; + NSString *expectedMediaHtml = @"[caption id=\"123\" align=\"aligncenter\"]existing caption[/caption]"; + MediaSettings *mediaSettings = [MediaSettings createMediaSettingsForUrl:@"http://www.someurl.com/image1.jpg" content:content]; + + // test + mediaSettings.linkHref = nil; + //NSLog(@"MediaSettings: %@", mediaSettings); + + // asserts + STAssertEqualObjects(expectedMediaHtml, [mediaSettings html], @"html"); +} +@end