Monday, December 13, 2010

Enforcing Record Name Convention in Salesforce without Apex

One can enforce a record naming convention with a few steps in Salesforce, sans Apex!
  1. Create a formula field called Expected Name with a formula that evaluates to the name you want to see.
  2. Create a validation rule called "Name Expected" with error formula: Name != Expected_Name__c

Enforcing Record Name Uniqueness in Salesforce without Apex

I discovered today that it is possible to enforce record name uniqueness in Salesforce without using any Apex code!

All that is needed is a simple validation rule with the error formula set to the following:
Name = VLOOKUP( $ObjectType.Object.Fields.Name , $ObjectType.Object.Fields.Name , Name ) && Id <> VLOOKUP( $ObjectType.Object.Fields.Id , $ObjectType.Object.Fields.Name , Name )

However, there doesn't seem to be an easy to to perform a case-sensitive comparison in the formula builder. Another thought would be to do the following to enforce name uniqueness:
  1. Create a text field called Unique Name on the object, and add the case-sensitive uniqueness constraint.
  2. Create a workflow field update called Unique Name that just sets Unique_Name__c to Name.
  3. Create a workflow rule called Set Unique Name that's triggered every time a record is created or updated.
  4. Activate the workflow rule.

Last edited on 8/10/2011.

Friday, December 10, 2010

Field Update in Before Trigger Propagates to After Trigger

I confirmed a simple fact in version 20.0 Apex triggers today, that will guide me when writing future trigger code.

If a lookup field is not specified in the DML operation and will not be populated via any other non-trigger means, then a System.debug(Trigger.new); will show that the trigger does not have access to the lookup field value.

However, if the lookup field is populated or updated in the before trigger, then the after trigger will be able to access the new value!

For example, I have a Campaign Feed object that has a Lookup(Campaign) field with an API name of Campaign__c. In my before insert trigger on Campaign_Feed__c, I set Campaign__c to the Id of a newly created Campaign. As a result I am able to then use Campaign__c in my after insert trigger, even though I didn't initially specify a Campaign__c value when inserting the Campaign Feed record.

Wednesday, November 10, 2010

NO-SOFTWARE Tutorial for Salesforce Web Services

First... the gripes against Salesforce. Please feel free to open the actual tutorial if you're not interested in reading about how a billion-dollar company published an API with a lack of documentation that would induce balding in even a teenage developer.

16 man-hours of angst and frustration with Salesforce finally yielded a fruit that I can eat and then sleep on. Who would've thought that Salesforce, in its infinite wisdom, with its "NO SOFTWARE" slogan, would overlook the need for a tutorial that shows how to use the Salesforce WSDL in conjunction with the Web Services API. I certainly didn't expect this lack of documentation and support (which is Premier Support in my case).

As a newbie who has never touched a web service before with an immediate and urgent need to call one from my Salesforce org, I had no idea where to begin. Furthermore, my hopes were dashed when I discovered that the Quick Start chapter in the Web Services API Developer's Guide used Apache Axis or Microsoft Visual Studio as an integral part of the tutorial. What happened to "NO SOFTWARE"? Doesn't the Salesforce platform support both inbound messages and outbound messages for web services?

My chagrin was further exacerbated when I discovered to my extreme disappointment that neither the Salesforce-generated Enterprise WSDL nor the Partner WSDL could be directly imported back into Salesforce using the Generate from WSDL feature for Apex classes. Now, where have I encountered a very similar irony... Oh, yeah! I wrote about it in a previous post, "Salesforce CSV Format for Report Exports Is Not IETF RFC 4180-compliant".

Needless to say, when I thought that my first foray into web services would be made less painful by using Salesforce as the learning platform, I thought very, very wrongly. Well, I shouldn't say that, since the experience could've been worse if I had to write two web services from scratch in Java or something.

Anyway, thanks to an abundance of hope and the following inspirations found on the Internet, I believe I now have a workable tutorial that newbies like myself can use to familiarize themselves with using web services in Salesforce.
JavaRanch

Force.com Discussion Boards
If this tutorial helps even one person, I will consider the 16 hours well spent.

Thursday, October 21, 2010

Default Status for Emails Synced through Salesforce for Outlook

The default Status value that is assigned to emails synced via Salesforce for Outlook is not the oldest Status value, but rather the first Status value on the list that marks an Activity as Completed.

For example, the Status picklist had the following options:
  1. Not Started
  2. In Progress
  3. Completed
Then, I inserted a new option before "Completed":
  1. Not Started
  2. In Progress
  3. Completed through another activity
  4. Completed
When I sync an email with Salesforce for Outlook, the email is not recorded with Status set to "Completed". Instead, the email is now logged with Status set to "Completed through another activity".

To fix this problem, all I have to do is reorder the picklist so that "Completed" comes before "Completed through another activity". There is no need to delete any picklist options and then recreate them.

Wednesday, October 20, 2010

IETF RFC 4180-compliant CSV Reader for Salesforce

After several failed attempts at creating an IETF RFC 4180-compliant CSV reader for Salesforce, I finally have a candidate of an Apex Class that may be able to fit the bill for reading a compliant CSV file and returning a nested List where the outer List contains row records, and the inner List contains the sequential values in that row.

Edit

Posting the code within a PRE tag did not work. Let's try this download link instead.

Tuesday, August 24, 2010

Passing Data between Visualforce Pages with Controllers and Extensions

I spent a fair amount of time yesterday trying to figure out how to pass information back and forth between Visualforce pages, despite finding a pretty good article in the Force.com IDE Library.

"Creating a Wizard with Visualforce Pages"
http://www.salesforce.com/us/developer/docs/cookbook/Content/vf_wizard.htm

The article got me started, but then when I moved to the actual implementation in which I wanted to use controller extensions, I would be able to pass data between pages until I started including the extensions. I was left scratching away at my head for a while.

Finally, after many trials and errors, I came to the following conclusion: In order for data to be maintained from page to page, both the controller and the extension(s) referenced must be the same across all pages in the group.

For example, assume that the following Apex classes exist:
  • MyController
  • MyPage1Ext
  • MyPage2Ext

Also assume that I am trying to pass data between the following two pages, each of which uses the functionality included in the corresponding extension:
  • MyPage1
  • MyPage2

If I specify different attributes extensions="MyPage1Ext" and extensions="MyPage2Ext" for the two pages, then I am unable to pass data back and forth even if I specify the same controller. The trick is to use the same controller and the same extensions on both pages via the attribute extensions="MyPage1Ext,MyPage2Ext".

This is a bit depressing to know, but at least now I have a framework within I can build my actual application.

Wednesday, August 18, 2010

How to Fix Backspace in SSH Session to Unix ksh Terminal

I've been annoyed for a while now by the fact that when I start an SSH session to a AIX server that uses KornShell (ksh), my backspace key no longer deletes the character immediately preceding the cursor.

Luckily, I came across a forum thread that gave a solution for this annoyance.

"KSH Terminal Settings"
http://www.unix.com/unix-dummies-questions-answers/25310-ksh-terminal-settings.html

What I found out was that I could start the SSH session and then type the command stty erase ^H, which would enable me to delete correctly both within the SSH session and in my native Mac OS X Terminal after I ended the session.

The only (possible) problem is: Now that I've changed the erase character on the server, it appears to have "stuck". So, I hope other people who are accessing the server don't suddenly find that their backspace keys have now stopped working...

Monday, August 9, 2010

Salesforce CSV Format for Report Exports Is Not IETF RFC 4180-compliant

At the time of writing this post, I believe the published standard for CSV file formatting is the IETF RFC 4180.

Also at the time of writing this post, I believe that the Summer 2010 version Salesforce does not produce IETF RFC 4180-compliant CSV files for exported reports.

Here are a few differences in the Salesforce CSV file:
  • Salesforce uses a single LF ('\n') to start a new row of data instead of a CRLF ('\r\n').

  • Salesforce exports line breaks in street addresses with just a LF instead of the CRLF specified by the RFC.


A few more noteworthy considerations for the Salesforce report CSV files:
  • All values are delimited with double-quotes.

  • Salesforce does double double-quotes for values that contain the double-quotes character, as stipulated in the RFC.

  • For Text Area fields, Salesforce does convert line breaks in the field value to CRLF sequences, with the exception of native Street fields.

  • There is a footer in the CSV file that does not contain any data at all, which may complicate straight data imports into other systems or programs like Microsoft Access.

Wednesday, August 4, 2010

Putting a Name with a User ID in DegreeWorks

I came across a question once about what users all exist in DegreeWorks. I couldn't figure out the issue at first, so I created an SR with the AL and got some pointers that ultimately led me to the following query:

select shp_access_id, rad_name
from shp_user_mst, rad_primary_mst
where shp_access_id=rad_id
order by shp_access_id;


This query showed me the names associated with each of the logins to DegreeWorks.

On a side note: The DegreeWorks schema is not documented in any of the PDF publications. Instead, the schema is explained in files that are created during the server installation inside the .../app/schema/ directory:
  • dapdb
  • raddb
  • shpdb

Tuesday, July 27, 2010

Salesforce, Internet Explorer 8 and the Missing DOCTYPE Declaration

All of my frustration with IE8 is now divided 25-75 between IE8 and Salesforce, with most of the frustration on Salesforce.

The layout scheme I described in my previous post works in all browsers except in IE8 when implemented as a Salesforce Visualforce page. Offline, I was able to reproduce the issue with the source code saved from the offending Visualforce page. Then, furthermore I was able to fix the issue by adding a simple DOCTYPE declaration at the top of the page.

The layout issues appear to be caused by the fact that Salesforce does not generate a !DOCTYPE tag at the top of the page, and IE8 just happens to assume a different !DOCTYPE than Firefox or Chrome. The !DOCTYPE that IE8 assumes must not be compatible with CSS 2.1 and HTML 4.1, the standards that I am trying to follow (although I'm probably making some mistakes along the way as well).

Regardless, Salesforce's Developerforce article "Using the Salesforce CSS in Your Apps" explicitly states (at my last viewing of the page on July 27, 2010 at 6:13 PM EDT):

Please ensure that you define the following DOCTYPE at the top of your HTML:

<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">


Ironically, if I try to add the !DOCTYPE tag before the apex:page tag, I get the following error:

Error: java.lang.NullPointerException
Error: null


Furthermore, if I try to add the !DOCTYPE tag immediately after the apex:page tag, I get a different error:

Error: A DOCTYPE is not allowed in content. at line 2


I found a Salesforce Community thread that talked about this issue, but the resolution is ambiguous to me and does not clearly show how to get the infinitely important !DOCTYPE tag into the page. The thread "Changing doctype of a Visualforce Page" shows many people having success with getting the !DOCTYPE tag to stick, but for some reason it is still not working for me.

I'm going to log a case with Premier Support and poke around some more to see if I can't get this to work in the next 15 minutes before the lights shut off here in the office.

Sunday, July 25, 2010

Better Understanding of Using CSS for Page Layouts

After hours and hours of exhaustion and frustration, I've finally arrived at what I can accept as a decent implementation of a webpage with a gradient border.



The page header and page footer will stay in-position with the page, and the footer will always appear at the bottom of the screen no matter how it is resized or how much content is on the page.

The final implementation has code that looks like the following in the HTML:

<!-- Create the backdrop. -->

<div class="Backdrop">
<div class="BackdropLeft">
</div>
<div class="BackdropRight">
</div>
</div>

<!-- Create the page (containing content). -->

<div class="Page">
<div class="PageHeader">
PageHeader
</div>
<div class="PageBody">
PageBody
</div>
<div class="PageFooter">
PageFooter
</div>
</div>

Then, CSS in the background was used to do the trick to position everything correctly.

body {
background-color:#987431;
} /* body */
body>div {
width:800px;
} /* body>div */
div.Backdrop {
position:fixed;

left:0;
margin-left:auto;
margin-right:auto;
right:0;

top:0;
margin-top:0;
height:100%;
margin-bottom:0;

background-color:white;
} /* div.Backdrop */
div.Backdrop>div {
position:fixed;

width:100%;

height:100%;
} /* div.Backdrop>div */
div.BackdropLeft {
background-position:0;
background-image:url("gradient-rl-nu_black-nu_gold.jpg");
background-repeat:repeat-y;
} /* div.BackdropLeft */
div.BackdropRight {
background-position:760px;
background-image:url("gradient-lr-nu_black-nu_gold.jpg");
background-repeat:repeat-y;
} /* div.BackdropRight */
div.Page {
position:absolute;

left:0;
margin-left:auto;
margin-right:auto;
right:0;

top:0;
margin-top:0;
margin-bottom:0;

min-height:100%;
} /* div.Page */
div.Page>div {
left:0;
margin-left:auto;
width:720px;
margin-right:auto;
right:0;
} /* div.Page>div */
div.PageHeader {
position:absolute;

top:0;
margin-top:0;
height:100px;

background-color:red;
} /* div.PageHeader */
div.PageBody {
background-color:white;

padding-top:100px;
padding-bottom:100px;
} /* div.PageBody */
div.PageFooter {
position:absolute;

height:100px;
margin-bottom:0;
bottom:0px;

background-color:blue;
} /* div.PageFooter */

I feel like I have a much better idea of how to layout webpages now. Just learning all of the available tools (properties) is a huge challenge, which included almost 100 pages of reading on the W3C website for the CSS 2.1 specification. Whew!

Wednesday, July 21, 2010

apex:actionSupport Does Not Work in Form with Required apex:input Components

After spending over two hours troubleshooting what seemed like a ridiculous problem, I discovered that the apex:actionSupport component does not work if any other apex:input* components have the required attribute set to true.

Unbelievable, the kind of stuff that's missing from the Visualforce documentation...

Monday, July 19, 2010

Tips on Getting Started with Ajax in Salesforce

It appears that there are a few things to keep in mind in order to reduce frustration and wasted time when implementing AJAX behavior with Salesforce.

  • The reRender attribute only works with apex:output* standard components.

  • The apex:outputPanel standard component produces a DIV tag, which means that it cannot be used to encapsulate table rows or table cells. However, the component can be used within a table cell.



I don't know why, but it took me a few hours to figure this out while I was trying to achieve Ajax behavior with as few Apex components as possible.

Tuesday, July 13, 2010

How to Exclude Address Lists from Outlook Autocomplete

Problem:

I received a rather unhappy email from a student today regarding an email on which that student was incorrectly copied. It turns out that this student's name is the same as the contact in my Outlook address book that I was trying to reach.

Further testing revealed that when I composed a new email, my Outlook address book was never even consulted for autocomplete, since Outlook would automatically select the student's address without my confirmation. Pressing Ctrl+Enter like I usually do to quickly send out emails would cause the student's email address to be automatically selected with no indication to me that the selection was actually incorrect.

So... what to do to prevent myself from getting into trouble later...

Solution:

Fortunately, I came across a Microsoft article that gave me a hint after I searched for "microsoft create a filtered global address list" on Google.

"HOW TO: Create a New Global Address List with a Custom Search by Using an LDAP Filter"
Microsoft Support
http://support.microsoft.com/kb/823151

The article gave me hope, and I looked more into my address book settings where I found the answer.

So, without further ado, the steps to reconfigure the autocomplete settings are:

  1. Open the Address Book.

  2. Click Tools > Options.

  3. Add, remove and reorder the lists used by the check names and autocomplete features.

Saturday, June 12, 2010

FCKeditor Works with MediaWiki Now

For the longest time, our MediaWiki installation was abandoned because I could not figure out why FCKeditor would not work. The behavior was erratic at best, being that the issues were different depending on which browser I was using.

In Snow Leopard with Safari 4.0.5, the editor appeared to work, allowing me to create a new page and use the WYSIWYG editor as I pleased... until I tried to edit a page. When editing a page, by default the wikitext editor appears with the correct contents of the page. But as soon I switch to FCKeditor, all of the text disappears, leaving me with a blank slate. Then, when I switch back to the wikitext editor, all I see are six alarming characters: $nbsp;. Everything in the page was effectively erased.

In Snow Leopard with Firefox 3.6.3, FCKeditor would not load at all. Whenever I tried to switch to the rich text editor, I would get a totally mysterious error in the Error Console.

Error: uncaught exception: [Exception... "Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [nsIXMLHttpRequest.statusText]" nsresult: "0x80040111 (NS_ERROR_NOT_AVAILABLE)" location: "JS frame :: http://www.northeastern.edu/cpspedia/wiki/skins/common/ajax.js?207 :: anonymous :: line 124" data: no]


In Windows with IE8, FCKeditor would also failed to load with the same visual symptoms as experienced with Firefox in Snow Leopard. To Microsoft's credit, IE8 did produce a more useful error message than I had previously encountered.

Webpage error details

User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; InfoPath.2; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Timestamp: Sat, 12 Jun 2010 23:17:16 UTC


Message: Access is denied.

Line: 107
Char: 3
Code: 0
URI: http://www.northeastern.edu/cpspedia/wiki/skins/common/ajax.js?207


Months ago, I had searched high and low over the web and found nothing useful to help me solve this problem.

With renewed determination this time around, since there was a larger user buy-in and more at stake to have a successful deployment of MediaWiki, I tried to tackle the problem again. Hitting the same walls as I did previously, I was almost ready to give up and file a bug with CKEditor and cross my heart hoping to die that CKEditor would troubleshoot the issue with me. I even found a tip in the "FCKeditor integration guide" that sounded exactly like the solution for me, but it seemed suboptimal and more importantly did not work.

After spending almost three hours this time around on the same problem, I took one last shot by diving into wiki/skins/common/ajax.js. There, going to line 107, I found a lone function call with three parameters, one of which was called uri. When I used alert() to examine the three parameters, I almost jumped from my seat as I realized what the problem was.

The URI being passed to the function had an internal base server name and not the external server name that I was using to access the wiki in my browsers!

Digging through some MediaWiki documentation, I found a reference to $wgServer and found out that this variable was not set in LocalSettings.php. Once I set this variable with the correct server name, all was well.

Correction: All is well.

Goodness... what a trip.

Copy and Paste from Mac OS X Clipboard in VI

I really like using VI, but one of the frustrations for me was not knowing how to copy to and paste from the system clipboard. Thankfully, a search on Google took me to an article that gave a very good pointer on how to workaround the issue.

"Mac OS X clipboard sharing"
http://vim.wikia.com/wiki/Mac_OS_X_clipboard_sharing

Basically, the technique involved using VI's ability to execute shell commands within the context of the text editor. So, the copy/paste functions can be emulated by using the native pbcopy and pbpaste commands in Mac OS X.

  • To copy, type :[range]!pbcopy

  • To paste, type :r !pbpaste

Saturday, June 5, 2010

Terminal Setting to Facilitate VI in Mac OS X SSH Session

I was constantly annoyed by the fact that when I connected via SSH to a remote Unix server and tried to run VI or VIM, I would encounter the following message:

ex: 0602-108 xterm-color is not a recognized terminal type.
[Press return to continue]


After I acknowledge the error, VI would be essentially unusable.

Luckily for me, I was able to find a post from back in 2004 about the same issue on earlier versions of Mac.

"Setting terminal to be recognized for SSH."
http://forums.macosxhints.com/showthread.php?t=19246


While the suggestion they had for creating the ~/.ssh/environment file did not work for me, setting TERM=vt100 before I ran ssh in Terminal did allow me to use VI properly in the SSH session.

Friday, April 16, 2010

iPad Test Drive: Day 4.5 at Last

So, without much emotion, I handed the iPad back to Tom today after erasing all of my data from the device...

--

Goodbye, pretty, shiny piece of modern technology with no apparent business use. I wish I could justify having you at my desk all the time to make me look cool at the center of everyone's attention, but I simply can't at this moment. Your battery life is awesome; your screen resolution and graphical rendering capabilities are second-to-none; and you're probably one of the sexiest gadgets I've seen in a long time. But you're just not for me, or for any regular office-dwelling employee.

However, I would really like to try out your evil arch-nemisis (if it turns out to be that way): the WePad. Don't take offense. You were the first of your kind, and you definitely made a bold statement about the new age of computing to come.

Thanks for the ride.

Thursday, April 15, 2010

iPad Test Drive: Day 3.5

I bumped into a guy on the T today, and the next thing I knew he was asking me about my thoughts on "my" iPad. I explained that I was testing the iPad for work-related uses but that so far I'm drawing blanks (except for the two applications previously mentioned). He seemed really worried about my response and immediately pulled out his own iPad to show me his note-taking app. It turns out that he's a BU student who works at e Apple Store on Boylston. I'm happy for him that he's so loyal to his employer, but I really do think that the iPad is only a taste of the real tablet products to come.

A quick read of the iPad's upcoming competition gives me great hope that eventually I will have a tablet that is also a laptop replacement. Lenovo's take on the tablet is the closest to what I would consider the next-generation workplace computer, and everything else is a cool gadget with limited uses.

I still haven't thought of a paid app to try yet. Maybe something will come to me in my sleep tonight.

Wednesday, April 14, 2010

iPad Test Drive: Day 2.5

While it's sad to say that I probably can't justify getting one of these for my own personal use, I think that to Kevin and Leanda this iPad may be of some value for applications they've already identified. The keyword is "may", because a lot of functionality is theoretical at this moment and may even require in-house development.

Leanda believes that recruitment may benefit from using the device at live events to both woo prospects and facilitate quickener and more accurate data entry. This is dependent upon the ability to develop either a native app or a web app that enforces data validity and is easy-to-use.

Kevin sees that as course content consumption shifts more and more into the virtual realm, a device like the iPad would be the medium through which students view lectures, complete homework assignments and take tests. Again, the potential is there, but execution will depend on how user-friendly and functional the final app or web app truly is.

One good thing to report: I have to say that reading the pizza menu in PDF format on the iPad was a very enjoyable experience, although I do wish that I could've searched for keywords Ike "pepperoni".

I'll try to think of a useful paid app that I can try out tomorrow for a different twist on the iPad.

iPad Test Drive: MobileMe

Okay, so... MobleMe is an interesting product from Apple designed to get around the fact that you can't store files directly on the iPad and you can't connect it up to your computer as an external hard drive.  There are actually two apps that you have to download to use the online storage features that I personally thought could be all obtained within one app.

For instance, if you upload a picture from your iPhone to MobileMe, wouldn't you expect the picture to show up in the iDisk app's Picture section?  No, Apple didn't think so.  Apple wants you to download their second app, Gallery.

Fine, I can handle that.  But the Gallery app isn't even made for the iPad.  Apple simply took an iPhone app and threw it into the iPad section of the store.  Even the interface is iPhone-sized, forcing you to use the iPhone-style keypad for data entry instead of the native iPad keyboard.  This from the same company that couldn't be bothered to throw in foreign language input support that already exists for the iPhone...

But still!  No big deal, right?  Until you get to the final kicker of the whole ordeal:  There is no way to delete a picture that you've uploaded to MobileMe using either iDisk or Gallery.  You have to log on to the MobileMe website to manage or delete the pictures you uploaded.

Oh, and by the way, you can't do this from the iPad because Safari for iPad is not a real browser.    

Tuesday, April 13, 2010

iPad Test Drive: Day 1.5

This is so good that I simply couldn't wait to blog about it: a piece of technology for which I may happily pay $800, the WePad. Apparently other people also agree that a powerful piece of technology like the iPad shouldn't be locked down, monopolized and totally controlled by a company like Apple.

iPad Test Drive: Day 1

Hrmm... Interesting. So, when I attempted to start writing this blog using the iPad, I noticed that the rich text editor rendered but was unusable to me. I had to switch over to the HTML editor to start typing this stuff.

So... I've had the iPad for one day now since Tom gave it to me as part of the 1-week user experiments he will be conducting over the coming weeks. Hopefully through these experiments we will be able to find legitimate business uses for the iPad instead of using it as a glorified mousepad (which only works somewhat using the silicone cover).

Disclaimer: Any typos on this blog post is purely the result of typing on the iPad's digital keyboard and is not a reflection of my typing ability or my ability to spell.

Overall, my first impression with the iPad has been unimpressive. There definite are good features to the device, which I'll describe below.

  • The stuff on the screen is rendered beautifully. 100 points for presentation.

  • The battery life seems to be decent. At least it should last for a day under heavy use, because from wheat I've seen the iPad can run for even longer than the iPhone on a full charge.

  • I've started playing with Numbers, and my initial thought is that the spreadsheet program for a mobile device is pretty powerful. However, the real determination of how useful it is will be dependent on whether the iPad can become a laptop replacement for some people.

  • The ebook reader is really sweet, and the backlight isn't so bright that it hurts your eyes when you're reading. My wife is in love with this feature and is infinitely glad she didn't buy a Nook when that device first became available last Christmas.


Most everything that was sweet on the iPhone is sweeter on the iPad like Mail, Calendar and Contacts. But still...

I wish Safari for the iPad was a full-blown web browser with support for ActiveX controls, Flash and other rich-media technologies.
I wish Safari for the iPad had scroll bars for editing text in text areas.
I wish the iPad could sync with the iPhone to share pictures and notes.
I wish Calendar could be used to check room and invitee availability when scheduling meetings.
I wish Apple would release a scratch-protection cover for the iPad so that I'm not so paranoid about scratching the glossy touchscreen.
I wish there was an easily accessible file system browser like Finder.
I wish there was a Caps Lock key on the digital keypad.
I wish there was an apostrophe key on the same screen as the rest of the QWERTY keys.
I wish there were Chinese (Traditional) input options on the iPad like the ones they already have for the iPhone.
I wish I could write some kind of code for the iPad, using the iPad.
I wish I didn't feel like Bart Simpson in repeating these "I wish" phrases.