Archive for the ‘Code’ Category

Setting up SVN on MediaTemple Hosting

Saturday, December 26th, 2009

We finally set up our SVN on a MediaTemple grid server and there are a few tricks along the way that might help you set up yours.

For those that are unfamiliar with it, Subversion is a version control system that keeps track of current and previous versions of files. We use it mostly for developing web applications across multiple systems as it makes it very easy to work from different machines anywhere.

We wanted our setup to accomplish a few simple tasks:

  • Maintain a copy of our source code in a single, off-site repository.
  • Be able to easily download the current-state of any files to our laptops and desktops.
  • Be able to update the entire live website with a single file check-in command.

So without further ado, here's how to get started:

First, you have to enable SSH access on your administrator account. Log in to your MediaTemple account center and go to Server Administrator in your Controls. Make sure the SSH Option is set to "Enabled."

Enable SSH

Just a quick note before we get to the commands, the next steps make a lot of use of 3 key variables: your primary domain, your administrator username, and your grid identification number.

Primary Domain (herein, my-example.com)
The primary domain is usually the domain you first signed up with. In this example, it will be called "my-example.com" which means do not use the leading "www."

Administrator Username (herein, username)
As of the writing of this article, the default administrator username appears to match the primary domain (i.e. "my-example.com") but for some legacy users it may be something like "serveradmin@my-example.com". Whatever yours is, you'll want to make sure to use the entire name. To figure out what yours is, go to your controls and click "Server Guide" you'll find it under FTP -> Admin Username.

Grid ID Number (herein, #####)
If you don't know your grid id number, you can find it under "Server Guide" in your account center controls. It is the 4 or 5 digit number in your Primary Access Domain: http://s#####.gridserver.com/ (ignore the initial 's').

Next, you're going to log in to your grid server using SSH. To do that, you need to open up a Terminal window (/Applications/Utilities/Terminal.app), or using a client like PuTTY if you're on Windows, and type the following command:

ssh username@my-example.com

Type in your administrator password when prompted (not the same thing as your Account Center password). You'll get a welcome message if you did it right, and now you're logged in to your server.

Next, we're going to make a directory to collect your repositories in (in case you, like us, want to track multiple projects separately). Feel free to change out the last bit, "svn_repos," to whatever folder name you wish to use to collect them in. (For simplicity sake, don't use any space characters!)

mkdir /home/####/data/svn_repos
cd /home/####/data/svn_repos

Ok, now you're in your svn_repos directory. Time to make your SVN repository (it's easier than it sounds). Luckily for you, SVN comes preinstalled on your server. The repository we're making is going to manage our entire website so we're actually going to name it "my-example.com" you can name yours anything you like but if you want to update your site with a single checkin, we advise you do the same.

svnadmin create --fs-type fsfs my-example.com

The bit about "--fs-type fsfs" just tells SVN to use a filesystem type that (mt) supports. You're done with your server for now, be sure to logout.

logout

Now that your repository is created, it's time to do a checkout on your local machine.

This part of our tutorial is done using the command line version of SVN for Mac that is available from CollabNet (link). You'll want to get the "Universal Subversion Binaries for MAC OS X" download and install it.

If you're using Windows you'll probably be using a graphical client like Tortoise SVN. Unfortunately we don't have step-by-step for you right here, but if you follow along you can probably figure out how to set up your SVN in a similar manner. If nothing else, stay tuned for the juicy server-side help at the end!

Now that you have an SVN client installed, you need to choose where you're going to keep your repository on your local machine.

Helpful Hint:
This tip is how to store your repositories with a short system path to make check-ins more painless. It is not critical to setting up your repository and you can skip it!

We sometimes use a tool called MAMP to create a local web development environment with PHP and MySQL. In order to use it, our local web files have to be stored in MAMP's htdocs folder in /Applications/MAMP/htdocs/. So we want to checkout our repository into our website folder -but- that's way too long of a folder path to type out every time we want to check in our files! Enter: the symbolic link. A symbolic link is a file that links to a folder and acts transparently as that folder.

So we're going to create a "/web" link in the root of our hard drive. Open Terminal and enter the following command (where "/Applications/MAMP/htdocs" is the real folder you want to put your repositories in and "/web" is the shortcut folder name):

ln -s /Applications/MAMP/htdocs /web

Now whenever we want to get to the folder containing our repositories, we can just use /web/repository_name instead of that long folder path!

Ok, now go into the folder where you want to checkout your repository. (Note: When you checkout your repository, it will create a folder that has the same name as the repository. Navigate to the place that you want to hold that repository folder.) We're going to use /web - replace it with your desired container.

cd /web

Now, let's do the checkout. Type the following command in your Terminal window:

svn co svn+ssh://username@my-example.com/home/####/data/svn_repos/my-example.com

That's it! Your local SVN is now configured. Let's head back to the server for a moment:

ssh username@my-example.com

Now we're going to navigate to the location we want our web files to appear when we check them in. This is NOT the same as your SVN repository in the "data" folder. Instead, we're going to go into the "domains" folder where our websites files are hosted and can be seen from a web browser. Because we're setting up our entire domain to be under version control, we're going to navigate directly into the domains folder. If you only want a sub-directory of your website to be under version control, you would navigate to its parent directory in this step.

cd /home/#####/domains

Helpful Hint:
If you're setting up version control for your entire site, you probably have some files that are currently in your website's directory. If you're not setting up version control for your entire site, you can skip this step. Otherwise, move your website's folder to a backup location:

mv my-example.com bak.my-example.com

Now, checkout a copy of the repository to this folder on your server. The syntax is a little different than before.

svn co file:///home/#####/data/svn_repos/my-example.com

Helpful Hint:
If you're not setting up version control for your entire site, skip these steps. Otherwise, copy your website's files back into place:

cp -r bak.my-example.com/html my-example.com/html
cd my-example.com

After copying, sometimes the permissions on the html folder can go askew. Best to run a quick chmod to make sure your file permissions stay correct here.

chmod 755 html

Now you're going to add all your website files to version control.

svn add html

You'll see a big list go by of all the files that SVN is adding to version control. When the list finally finishes and you have a prompt again, it's time to do your first check-in.

svn ci -m "Initial Check In"

This may take a while depending on how many files you have and what size they are. When it finishes, update your local machine's SVN with all these newly added files. From a separate Terminal window, navigate on your local machine and perform an SVN update.

cd /web/my-example.com
svn up

Hooks!
The Bread and Butter of SVN on a Web Server

That's right, folks. For anyone reading this article that is new to SVN administration - this is the section that you've been waiting for. "How do I get my web server to update to the newest version when I check-in my files?" you've been wondering... Enter: hooks. Hooks are scripts that execute when a repository action occurs. For the purposes of updating a live site on check-in, we're going to use what's called a post-commit hook.

So, while SSH'd inside your server navigate to your repository's hooks:

cd /home/#####/data/svn_repos/my-example.com/hooks

Make a post-commit hook with the following command:

vi post-commit

Press "i" to get vi in the "insert" mode and enter the following into the new file (you may replace "/home/#####/domains/my-example.com" with the location of the checkout we did in the server-side checkout earlier. If you've been following the directions for making your whole site source controlled, this is the correct directory.)

#!/bin/sh
svn update /home/#####/domains/my-example.com

Press Escape to get vi out of the "insert" mode then type :wq and press enter to save and quit. Now you need to make the file executable.

chmod 755 post-commit

Finally, logout of MediaTemple.

logout

And... You're done! Your website is ready to update live every time you commit a change to your repository. Remember, you can have your repositories checked out on multiple machines, and anytime you check-in a change from anywhere, your website will update.

I hope the above has been clear. Please forgive this verbose and convoluted tutorial, the ones I used in trying to get this thing set up seem to have left steps out which caused me a few headaches.

Basic SVN Usage on your Local Machine
Here are a few commands you may find useful when you're using SVN on your local machine.

All of these commands should be used inside the directory you want them to influence.

Checkout
This will perform the initial checkout of the current version of the SVN repository and set up that repository on your local machine. You only ever need to do this once per repository on each machine.

svn co svn+ssh://username@my-example.com/path/to/repo/repo_name

Check-In
This will check-in any updated files on your local machine to the server. You can populate the "Message" field with a helpful note about what changed or leave it blank with ""

svn ci -m "Message"

Update
This will update any old files on your local machine with the most current version available from the server.

svn up

Add
When you create new files or folders, you must let SVN know to add them to version control. Do so with the add command (if you use a folder name in place of filename, svn will add the folder and recursively add any folders and files within it)

svn add filename

Remove
Similarly, if you want to delete a file, you must remove it from source control. This command will delete the file or folder on the local machine and on the server.

svn rm filename

Additional Resources:

Resize Multi-line Text to Fit UILabel on iPhone

Thursday, July 30th, 2009

The UILabel is a great tool for displaying lines of text in any view and is easy to set up using the provided Interface Builder. Unfortunately, there is a deceptive option in the Label Attributes that says "Font Size: Adjust To Fit." This is a great feature because it means that the iPhone will automatically resize your text down in order to fit all of it into the label. Unfortunately, this feature only works when your label's layout is set to single-line. If your text is multiline like ours was, it won't adjust the font size at all!

Well we did some brief searching around to locate a solution to this problem but ended up simply writing our own. We have the following situation:

  • We have dynamic text, the exact quantity of which varies.
  • We want to place that text in a fixed-width and fixed-height UILabel
  • We don't want the text to get cutoff if it doesn't fit perfectly

In order to meet this challenge, we wrote some simple code to automatically resize the text.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//Create a string with the text we want to display.
self.ourText = @"This is your variable-length string. Assign it any way you want!";
 
/* This is where we define the ideal font that the Label wants to use.
   Use the font you want to use and the largest font size you want to use. */
UIFont *font = [UIFont fontWithName:@"Marker Felt" size:28];
 
int i;
/* Time to calculate the needed font size.
   This for loop starts at the largest font size, and decreases by two point sizes (i=i-2)
   Until it either hits a size that will fit or hits the minimum size we want to allow (i > 10) */
for(i = 28; i > 10; i=i-2)
{
	// Set the new font size.
	font = [font fontWithSize:i];
	// You can log the size you're trying: NSLog(@"Trying size: %u", i);
 
	/* This step is important: We make a constraint box 
	   using only the fixed WIDTH of the UILabel. The height will
	   be checked later. */ 
	CGSize constraintSize = CGSizeMake(260.0f, MAXFLOAT);
 
	// This step checks how tall the label would be with the desired font.
	CGSize labelSize = [self.ourText sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
 
	/* Here is where you use the height requirement!
	   Set the value in the if statement to the height of your UILabel
	   If the label fits into your required height, it will break the loop
	   and use that font size. */
	if(labelSize.height <= 180.0f)
		break;
}
// You can see what size the function is using by outputting: NSLog(@"Best size is: %u", i);
 
// Set the UILabel's font to the newly adjusted font.
msg.font = font;
 
// Put the text into the UILabel outlet variable.
msg.text = self.ourText;

To use the above text, you need to make a "IBOutlet UILabel *msg;" which you assign as the UILabel's Reference Outlet from the interface builder.

And there you have it: easy automatic resizing of multi-line text to fit in your fixed-size UILabel for the iPhone using Objective-C!

Send Email from inside an iPhone App

Tuesday, July 14th, 2009

One of our first challenges with the Ninja Missions iPhone app was to make a simple and quick way for users to share missions with their friends. This is a valuable tool for viral marketing. Through past projects, we know how many people use the 'share this with a friend' links on websites. It's a surprisingly effective free marketing tool, so we wanted to add one to our iPhone.

Our first thought was: How cool would it be to allow people to text it to a friend?!

Unfortunately, Apple does not currently have an API that allows you to open a pre-filled SMS like they do for email. The only way to achieve this would be to use a SMS Gateway provider (like TM4B or Clickatell) and send the SMS through a web-call in the application.

We can open the SMS application through a link with the following code, but there is no way to fill in the message automatically. (See: Apple URL Scheme Reference)

1
2
<a href="sms:">Opens SMS Application</a>
<a href="sms:1-800-555-1234">Opens New SMS Message to 1-800-555-1234</a>

Or, open it from within an app with the following code:

1
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms:1-800-555-1234"]];

Clearly, just opening a blank text message wasn't going to work in this case, and the price of SMS Gateway Providers couldn't be justified until our app started generating a bit of revenue. So we decided to use the tried and true email link to open a premade email inside of the iPhone Mail application.

To Send Email from inside an iPhone App in Objective-C:

1
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto:test@test.com?subject=Subject%20Line&body=Body%20Text"]];

Simply put, the above code is the core function needed to open a new email message from within an iPhone App. However, this was not entirely what we needed. We needed to put a NSString generated from our data into the middle of a fixed chunk of text. To do that, we did this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- (IBAction)sendEmail {
	/* We want to include this variable text within the premade body. 
	   You could replace this variable text with a personalized message from the user or remove it. */
 
	/* First, encode the variable text (in this case, the mission) 
	   NOTE: self.theMission is our NSString object which contain's the unescaped mission text
	   Replace this variable with your own variable (if needed). */
	NSString *encodedMission = [self.theMission stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 
	/* Make a string that contains the first part of the static text.
	   We start off with the mailto:email?subject=subject&body=text format.
	   Stop right where you want to put in the variable's text.
	   ---------
	   To make this part easy, use a free web-based URL encoder like the one at: 
	   http://meyerweb.com/eric/tools/dencoder/
	   ---------
	   Our first half will give us:
	   To:Test@Test.com
	   Subject: Subject Text Here
	   Body: Fellow Ninja -
	   (line break)
	   */
	NSString *firstHalf = @"mailto:Test@Test.com?subject=Subject%20Text%20Here&body=Fellow%20Ninja%20-%0D%0A%0D%0A";
 
	/* And the second half of the email (the post-variable segment)
	   Encoded the same way we did the first half. This string will decode to:
	   (line break)
	   Yours,
	   (line break)
	   Ninja Master 
	   */
	NSString *lastHalf = @"%0D%0A%0D%0AYours%2C%0D%0A%0D%0ANinja%20Master";
 
	/* Now we concatenate the strings. */
	NSString *mailCode = [NSString stringWithFormat:@"%@%@%@", firstHalf, encodedMission, lastHalf];
 
	/* Now it's time to send it! The following code makes the above string into a 
	   URL and opens the URL causing the Mail program to launch with a premade email. */
	[[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailCode]];
}

We then bind this code to a button action in the interface builder.

It is important to note that this button will not do anything inside the iPhone simulator from the iPhone SDK because the simulator does not have a Mail App. We have to test the app on a developer's iPhone to test the function.

And it works!

iPhone Email Screenshot iPhone Email Screenshot