Moving wordpress from shared host to aws ec2

I did an experiment recently trying to move my wordpress website from a shared hosting site to Amazon aws ec2 micro instance (t1). The migration was mostly successful, I did not make the move eventually due to some technical and non-technical reasons. Nonetheless, I would like to share some of the lesson I learned from this process. I followed this blog post form smashmagazine as a blueprint for “moving wordpress”. Note I already have ubuntu installed on ec2, and has setup keys for my laptop.

1) Setup wordpress on new host. This is the link I followed to setup wordpress on ubuntu on my aws ec2. Another useful article is here.

2) Upload files: I used FileZilla (SFTP) for uploading the files. Here is the link regarding setup keys. Note the file permission is also very important to make wordpress work, which is especially important when moving the sites. More on this later.

3) File permission, one thing I noticed quickly is I don’t have any writing privilege after moving, e.g., update plug-ins etc. Did google quite a bit, and tries to change the file permissions using FileZilla. No luck. Eventually I read this from wordpress official doc. Quote:…(a) file ownership: all of your WordPress files must be owned by the user under which your web server executes. In other words, the owner of your WordPress files must match the user under which your web server executes. The web server user (named “apache”, “web”, “www”, “nobody”, or some such) is not necessarily the owner of your WordPress files…

I did “ps -ef|grep httpd” at my ubuntu server, and find the user for apache web server (not “root”). And changed the file owner to this user (via “chown” command).

4) MySQL server crash: I found this happens quite often (as often as once every 3 days). Turns out to be a memory problem for the t1 micro instance and apache web server and mysql fights for the memory, eventually mysql lost. The solution I found is add virtual memory to the system, and reduce the memory requirement for mysql in config file. Eventually I was able to have mysql run as long as 13 days without shutting down. Not idea, but meaningful improvement. Another idea, people talked about is use a lightweight http server such as this one.

5) MySQL database migration. Found out for large database, php export will not complete, and will end up with some junk html in the database file (.sql), it will fail during import. The workaround is do the export and import in the mysql command line. But I still have one problem with database, it appears the Chinese character got lost during this process. This problem along with the mysql server problem mentioned above, and the relative higher cost of AWS compared to shared host, made me decide to not switch at this time.

Other links:
Setup php/mysql, and phpMyAdmin.

Some tips for submit updated app to iTunes store post iOS8

With (before) the release of iOS8, I noticed the iTunes Connect web interface has also been changed. But today is the first time I submit my app (update) to the store for last few months. There are some changes.

1) First, there is no “ready to submit app” button in the iTunes Connect, instead we just create a new version at ITC;

2) After that, we load the app to the app store, I used good old Xcode 5.1.1, which is the minimum version Apple requires now. Now here is a tip when submit from Xcode organizer, when I run “validate” before submit, it says I could not change “bundle” number, which I have not. Looking more carefully, it appears the good old Xcode 5.1.1 got confused with which app profile to use, and it picked the wrong one all the time. So I manually corrected it. After that I was able to upload the binary.

3) There is one more step, the actual “submit for review”, in the past the step 2) is sufficient. Now there is this extra step. In this step, it will ask questions similar to the questions being asked when we do “ready to submit” in the ITC in the past (before this change). In my case, since the app has “iAd”, it insists me check the Ad identifier selection, and after I check that, it let me submit (note I will try couple times, eventually I started over). But it appears not let me check “no” in the Ad section. This appears to be a glitch, I was able to submit my other 2 apps without any problem when checking no for the Ad identifier. There is another glitch though, that is I have to quit the app, before pick the newly added build, and submit for review. In summary, it appears ITC web page needs some work.

I will report more when/if I find anything new next time I submit app update. I noticed there is new marketing collaterals such as the preview video, and screenshots for 4.7/5.5 inch screen. My gut feeling is those will be required after this transition period. So hurry up if you have some app or update to release :-)

(PS, 09-28-2014) One more thing, I noticed if I wanted to reject a binary (a build in new term), I need to create a new version. In the past, I could upload a newer binary with same version number. I think it’s probably a good idea to limit this back and forth. Note Apple added testFlight capability too. It should help app testing.

Set up Apple TV with AT&T u-verse

It seems the wifi router comes with AT&T u-verse does not work with Apple TV. I found one workaround, that is to turn off the build in Wifi, use an old wifi router (D-Link DI-524) for Wifi instead. But there is still problem with the mirroring from iPad to Apple TV. Since I still have my previous LinkSys wrt54g router, I set it up as second router behind the DI-524, and connecting both the Apple TV and iPad to wrt54g. That worked. Another side benefits is wrt54g is a newer/more powerful router, and could broadcast wider range.

There is another problem with setup AT&T u-verse which is not related to Apple TV. The problem is when creating new account at their web site, they prompt for secret question/answer, it appears they don’t take any space in the answer. It was a programming error (web page text field validation) in my opinion, since I have done some web programming lately. The workaround is not to type any space. Simple enough.

This is also after using cable internet for about 8 or 9 years, I am switching to AT&T. I was using their DSL before switching to cable. The main reason is their service is cheaper, another factor is the cable had some connection issues lately.

Why new Apple smart watch is not called iWatch?

Here is my theory. Years ago I heard people in China mentioned “Apple Phone” (苹果手机), for iPhone. This is particularly true for older generation as their grasp of English is not as good as younger generation. So this time they decided just go with Apple Watch 苹果手表.

Note this is also their fourth main product category innovation since year 2000 : iPod, iPhone and iPad. If they stick with iWatch, the name will be too predicable. But this is secondary compared to the reason above, which is use their Apple logo, and name. Both are well known all over the world.

PS, saw this disturbing video about people lining up at Apple store for iPhone just for resell.

Calling restful POST PUT and DELETE methods in AFNetworking 2.0

Some example code here. For GET methods and AFNetworking 2.0 using a php web service, raywenderlich.com has an excellent tutorial. One problem I saw when I did this exercise is the service was expecting “application/json” for “Content-Type” (note the default is text/html, if we don’t specify in request serializer).


// POST method
-(IBAction)addBook:(id)sender
{
    // 1, note this is the base URL, typically it's the end point of REST web service
    NSURL *baseURL = [NSURL URLWithString:BaseURLString];
    NSDictionary *parameters = @{@"title": @"One Flew Over the Cuckoo's Nest",
                                 @"author": @"Ken Kesey",
                                 @"read": @false};
    
    // 2
    AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    
    [manager POST:@"/book" parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
        self.title = @"Book added";
        _textView.text = [[NSString alloc] initWithFormat:@"Response JSON: %@", (NSDictionary *)responseObject];
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        NSLog(@"\n============== ERROR ====\n%@",error.userInfo);
        
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error adding book"
                                                                message:[error localizedDescription]
                                                               delegate:nil
                                                      cancelButtonTitle:@"Ok"
                                                      otherButtonTitles:nil];
        [alertView show];
    }];
}

- (void)setupForRemoveAndUpdateBook:(AFHTTPSessionManager **)manager_p bookIdString_p:(NSString **)bookIdString_p
{
    NSURL *baseURL = [NSURL URLWithString:BaseURLString];
    
    [_textField resignFirstResponder];
    
    int bookId = [_textField.text intValue];
    
    *bookIdString_p = [NSString stringWithFormat:@"/book/%i", bookId];
    
    *manager_p = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];
    (*manager_p).requestSerializer = [AFJSONRequestSerializer serializer];
    (*manager_p).responseSerializer = [AFJSONResponseSerializer serializer];
}

// DELETE method
- (void)remove_book
{
    NSString *bookIdString;
    AFHTTPSessionManager *manager;
    [self setupForRemoveAndUpdateBook:&manager bookIdString_p:&bookIdString];
    
    [manager DELETE:bookIdString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
        self.title = @"Book deleted";
        _textView.text = [[NSString alloc] initWithFormat:@"Response JSON: %@", (NSDictionary *)responseObject];
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        NSLog(@"\n============== ERROR ====\n%@",error.userInfo);
        
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error deleting book"
                                                            message:[error localizedDescription]
                                                           delegate:nil
                                                  cancelButtonTitle:@"Ok"
                                                  otherButtonTitles:nil];
        [alertView show];
    }];
}

-(IBAction)removeBook:(id)sender
{
    [self clear:nil];

    if (_textField.text == NULL || _textField.text.length==0) {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Book ID required"
                                                            message:@"Please input the book ID"
                                                           delegate:nil
                                                  cancelButtonTitle:@"Ok"
                                                  otherButtonTitles:nil];
        [alertView show];
    }
    else {
        [self remove_book];
    }
}

// PUT method
- (void)update_book
{
    NSString *bookIdString;
    AFHTTPSessionManager *manager;
    [self setupForRemoveAndUpdateBook:&manager bookIdString_p:&bookIdString];
    
    NSDictionary *parameters = @{@"read": @true};
    
    [manager PUT:bookIdString parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
        self.title = @"Book updated";
        _textView.text = [[NSString alloc] initWithFormat:@"Response JSON: %@", (NSDictionary *)responseObject];
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        NSLog(@"\n============== ERROR ====\n%@",error.userInfo);
        
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error updating book"
                                                            message:[error localizedDescription]
                                                           delegate:nil
                                                  cancelButtonTitle:@"Ok"
                                                  otherButtonTitles:nil];
        [alertView show];
    }];
}

-(IBAction)updateBook:(id)sender
{
    [self clear:nil];

    if (_textField.text == NULL || _textField.text.length==0) {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Book ID required"
                                                            message:@"Please input the book ID"
                                                           delegate:nil
                                                  cancelButtonTitle:@"Ok"
                                                  otherButtonTitles:nil];
        [alertView show];
    }
    else {
        [self update_book];
    }
}

iOS Objective-C interview questions

I just throw some technical questions out here. Note Ray Wenderlich has a series on this. BlackPixel has an article too.

life cycle of UIViewController;

NSString property copy or retain?

frame vs bounds

category vs subclass

What is protocol (Apple doc; Ry’s Objective-C tutorial)? Create a protocol for List, and how to implement the “add” method for linked list (stackoverflow)?

block (how to avoid memory issue);

To be continued…

Update sqlite database during iOS app update

We all know updating sqlite database on an existing iOS app could be tricky, esp. with any schema (table) changes. In the good old days when I was developing in-house app, I found out the app would crash when I added a column to the sqlite database, after merely updating the app. The problem was, the old database won’t get updated if we don’t explicitly do it during app start-up (after app update). So what did I tell my user to do? Delete the app, and re-install the app. Not the most user-friendly way, and what if the user has some data on it that he/she wants to keep? What if this app have many users?

I had this updating issue in my mind for a while, because one of my apps needs to refresh its data. There are a few ways to do it: 1) Use a web service to get the data; 2) Just update the sqlite database. The former has some limitations: we could get data via web service, but note we still need to manipulate the database if we need to add a column or table (this is same for both approaches). The latter is faster in terms of development. So I decided to do it. I did some research on stackoverflow and found this post as my reference. Here is my implementation, if you are interested. The following is done in viewDidLoad.

Continue reading

stlplace.com was hacked and restored, plus some thoughts

I found out this site was hacked yesterday evening, as I saw the loading of site on Safari was slower than usual. It also shows incorrect theme, more like a plain theme. With the admin link redirect to spammer site. I decided to tackle it right away. I recall about one year ago something similar happened to this site, and google webmaster tool told me about it. I was able to remove the offending files/directories, by following the recommendation set out by google and some other wordpress sites.

This time I made an almost fatal mistake, during deleting some of the files in wp-includes, I accidentally deleted all the useful php files there. Panicked, I used both the website restore tool, and the import feature of wordpress (mojo marketplace), to no avail. The symptom of the problem was I could not login, and it shows blank page when I login via the wp-admin or wp-login.php after taking my credential. And I can only see the pages at uudaddy.com (I could not log in there either).

I filed for help at the hosting company. But I still feel helpless as this site has about 9 years of my blogging and uudaddy.com has my last 4 years of blogs. Fortunately I was able to find this wordpress help page about updating wordpress, and fix internal server error by deactivating the plug-ins. The latter comes only after I gained some web dev experience lately, knowing more about error 500 :-)

So long story short, I was able to restore all the blogs (those two plus my wife’s happy mandarin.com) by the following:
1) Restore .htaccess file to avoid the redirect to spammer page;
2) Restoring the wp-includes and wp-admin page: upload zip file, extract; in the wp-content directory, I renamed plugin directory as plugin.SAV (this way it deactivates all the plugins);
3) Run the wordpress update as soon as I can log in.

I also backed up the MySQL databases for stlplace and uudaddy, and backed up those two blogs to wordpress.com (this one and this one). I understand blogging itself is a declining trend, and it’s probably not easy for small web hosting company to defend against hackers like Google/Amazon/Wordpress do. That’s why I am also evaluating whether to move to wordpress.com or Amazon EC2 (self hosting). It’s a bit emotional decision as I have hosted this site for 9 years, but I need to consider both my time, the cost of hosting, etc. I will make a decision on this shortly. Meanwhile check out the new blogging sites I mentioned above, in which I will blog both about software development and raising kids.

Rockstar developer

I have thought about this topic for a long time. I remember seeing at one place that an exceptional developer can do work usually done by 10 developers. Or in other words, he/she is a rockstar developer.

Besides productivity, I think rockstar developer has the following traits:

1) Willing to share the knowledge with fellow developers, keep in mind we all learn from each other, rockstar can learn from (dare I say) ordinary developer too;

2) Attention to the details and code quality, and other good development practice such as TDD (test driven development), again sharing knowledge is applicable here;

Continue reading

Developer EQ series : 1

Dont’s
Beat up people in code review or interview;

Change other developers’ code without proper communication, or reasoning; worse, making the working code no longer work (sometimes to satisfying the “tests” :-)

Do’s
Learn, learn something everyday;

Acknowledge our own limitations: acknowledge our mistakes if applicable, we all do make mistakes;

In the same token, when we learn something from someone else, show our appreciation;

PS, also came across this piece “assertiveness from developers” by Jeff Atwood (@codinghorror), which I think is excellent. Essentially the point there is be a good developer is not to be a “nice person to everyone”. A good developer uses his/her best judgement, and speaks from his/her own mind, which includes saying something make other stakeholder uncomfortable (as long as it’s fair and honest statement).

And, slightly off topic, this one “Your Company Is Not A Family” from HBR (Harvard Business Review).