Wed, 13 Dec 2006
If you are investigating the move from Mac OS 9 to Mac OS X for your computers and network there is a great document from Apple that outlines the nuts-and-bolts steps required.
Migrating to OS X is a big step however, so planning and research are imperative steps. We also strongly believe that the move to OS X should be viewed as an oportunity to improve your process not just upgrade your machines. So, here are some tips to get you thinking:
Research, research, research. We cannot say it enough. Audit both your hardware and software thoroughly. Make sure not to miss expansion cards, peripherals, utilities, and plug-ins.
To check for you're software's OS X compatibility, use sites like Versiontracker or MacUpdate. Be sure to check the readers reviews while you're there.
Check with each of your peripheral and expansion card manufacturers to find out their OS X compatibility. If you own a scanner that does not have a driver update for OS X check VueScan or SilverFast to see if they offer support.
Start with one machine. Test it thoroughly, make sure that each step of your workflow can be completed successfully. Hand it off to one of your most adventurous users, let them put it through its paces.
If you go looking for problems you will find them. Obviously, while embarked on any task as big as moving your office from one operating system to another you will encounter problems, try to see passed them. Look for opportunities, OS X is an incredibly rich platform, there are plenty to be found.
The move to OS X automatically gives you access to expanded scripting opportunities [AppleScript, Unix Shell], fast as wire gigabit network speeds, enterprise class OS X Server, and more. Each one provides major opportunities to tune, optimize and enhance your workflow. Use it to your advantage.
Use OS X's capabilities to help you migrate! Netboot, NetInstall, Apple Remote Desktop, Radmind, NetRestore, Carbon Copy Cloner - and more, the list is endless.
There is lots of information on the web, use it. As someone smarter than me once said: "Find out where the smart people are and hang out with them". Here are a few places to start Bombich Software, Mac OS X Labs, afp548.com.
If the entire thing feels just too big to handle, don't despair there are people willing to help. Codeferous Software Solutions offers a full OS X Migration service. Give us a call and we'll be happy to quote..
I just finished teaching a short OS X Troubleshooting course, and answered a number of questions pertaining to permissions on Mac OS X. This highlighted the fact that many people in graphics and print workflow's are struggling with these issues. So I figured I'd take a short break from the AppleScript articles to focus on permissions for the workgroup.
Permissions and privileges are integral to Mac OS X. Every folder and file posseses them, and they govern their access by every user. The default permissions mask (umask) for Jaguar and Panther err on the side of privacy, which makes perfect sense for most individuals and groups, but not for one particular group - those involved in a collaborative workflow.
By default any new file or folder created by the Finder or any other application posseses the following permissions:
So, as the owner of the file I can do anything I want with it. Members of my group (staff in this case) and everyone else can read the file but not modify it. There are many cases where these are the correct permissions to grant for a folder or file. For example; as a student you don't really want everyone else in the Student group to be able to modify your stuff do you?
Where these default permissions fail however, is when you are involved in a collaborative workflow. In that case you want permissions that looks something like this:
For a collaberative workgroup this makes a little more sense; I remain the owner of the file, but now my group has the ability to work with and modify it as well. Unfortunatley OS X does not give us a GUI to change the default permissions behaviour. So, many workgroups face grappling with this issue everyday. Apple will take great pains to explain that this is a security feature of OS X and I tend to agree, but I know many of my clients view it as a bug.
There are several solutions to this problem and we are going to investigate them in the remainder of this article. The potential solutions fall into the following categories:
The right one for you will depend on your workflow, Mac OS X version, and user base. Read on and we'll help you make the right choice.
As always, the easiest solution to configure is the manual one. Train your users to manually modify the permissions of any file or folder they create so that their group can have access. They can do this from within the Owner & Permsissions section of the Finder's File Info window.
Pros: Easy to implement and provides infinite control over the permissions.
Cons: Labour intensive. Relies on user compliance. Not an attractive solution to any designers I've ever worked with.
Cron is a unix daemon that runs periodic tasks. OS X uses it to run scheduled maintenance scripts among other things. We could use it to automatically set the group permissions on a given directories contents every 5-10 minutes saving our users from having to do it manually. This could be done on a local workstation if it is being shared by multiple users, or on the server for a shared volume being used by a workgroup.
Cron is controlled by a file called a crontab and there can be several of these. The one we are interested in is the System (or root) crontab that exists at /etc/crontab. This Mac OS X Hints article does a decent job of explaining how to configure cron, and this article describes how one user used cron to apply this kind of permissions fix.
As the previous link shows, we can configure cron by editing a text file through the Terminal, but thankfully there is a great GUI utility to do this for us. Go and grab a copy of Cronnix, a GUI tool to configure cron, and once it is installed launch it.
With Cronnix running select Open System Crontab from the File menu and click on the New icon in that window's tool bar. Configure the new task to run every 5 minutes by setting the Schedule section of the window so that it looks like this:
We want the task to run the chmod -R command on our shared folder, so in the Command section of the window enter:
/bin/chmod -R ug+rw /Path/to/shared/folder/
Click on the New button and then Save in the toolbar - you will need to authenticate for the changes to save. If you have multiple shared folders you will need to create multiple cron tasks to address each one.
Pros: Requires no effort on the part of your users. Effect limited to specified directories. Doesn't modify default behaviour of workstations.
Cons: Must be configured for every server and share point. Not immediate. Could result in some performance penalty.
The umask specifies the default behaviour of OS X when creating new folders or files. By default the umask on OS X is 022 which means that files and folders are always created with read-only permissions for Group and Others. The umask can easily be changed using the Terminal to 002 which means that only Others is masked to read-only. Unfortunately this only applies to shell sessions and has no effect on the Finder or any other GUI applications. To get this to work for all of our applications we have two solutions, depending on whether we are running Jaguar or Panther.
Jaguar: To modify the umask settings in Jaguar you need to install a modification called global-umask by Len Laughridge, you can find it here. Be sure to read the instructions and warnings carefully. This modification significantly changes the boot process of your Jaguar machines so it is important to understand what it is doing, and how to remove it.
Panther: Modifying the umask setting in Panther is a little easier, we can use the latest version of Marcel Bresink's TinkerTool. Launch TinkerTool and click on the Permissions icon in the toolbar. These are the default settings:
Unselect the checkbox in the Write file. Create or delete objects collumn for Group. Close the TinkerTool window and logout (and back in again) for the changes to take effect. All new files and folders created by the finder or other GUI applications will now have read/write permissions for both the User and Group.
A post on the macos-x-server list alerted me to an interesting new Mac OS X Hints article describing how to modify the umask globally on Panther machines (rather than per user as TinkerTool does).
Pros: Requires no effort on the part of your users. Works on local and server based files and folders.
Cons: Must be configured for every workstation. Changes the default behaviour of the workstation in a way that may be perceived as a major security breach.
OS X Server: Apple provides a mechanism in OS X Server 10.2.4 and newer that allows greater control of the permission inheritance of connected (10.2.4 and greater) clients. On the server, launch WorkGroup Manager, select Sharing from the toolbar and select a share point. At the bottom of the Protocols tab you will see a pair of radio buttons like this:
By default Use standard Unix behavior will be selected. By selecting Inherit permissions from parent we can force all new files and folders created in this share point to inherit its permissions. So if our share point has read/write permissions for the group, all its children (if created on the server) will have them as well.
OS X Client Many smaller organizations don't have centralized file servers, relying instead on ad-hock networks of workstations using personal file-sharing. Luckily the great shareware universe provides for them as well. Michael Horn's excellent utility SharePoints not only returns much of the lost functionality of Mac OS 9's personal file-sharing, it also returns Groups creation and adds SMB configuration as well. Most importantly for us, it also enables the Inherit Permissions functionality of Mac OS X Server on Mac OS X Clients hosting personal file-sharing.
First we need to create a new share. Launch the SharePoints application, and select the "Normal" Shares tab. Enter a Share Name and select a directory to share (enter the path or use the Browse button). Enable AppleFileServer (AFS) Sharing: and click on the Create New Share button to create our new share point.
Select the new share when it shows up in the list, and click on the Show File System Properties button:
Giving the Group r/w permissions and selecting the Inherit permissions from parent checkbox will cause this share to behave exactly the same way as if you had configured it with Workgroup Manager on Mac OS X Server.
Pros: Requires no effort on the part of your users.
Cons: Must be configured for every share point. Only works for network mounted volumes.
Like many problems in Mac OS X, there are many ways to work around the permission issues faced by collaborative workgroups. I hope this article gives you enough to choose a solution suitable for your environment. Next week we'll return with another Workflow AppleScripting article, untill then....
Since my last article on wrangling OS X permissions I've discovered that there is a substantial change in the way Panther handles group assignment. In Jaguar, by default, all users had staff as their primary group, in Panther, each new user gets their own unique group. This is a major change for those needing to share files on a local machine.
Note: You won't see this for any pre-existing users on a machine upgraded from Jaguar, only on new users, or clean installations.
For instance, if we create a new user in Panther called test and look at the user details in SharePoints* we'll see something like this:
* Disclaimer: Of course you could always use /Applications/Utilties/NetInfo Manager, which will show you the same info (and more), but SharePoints just makes me feel all warm and fuzzy inside...
Notice that the UID for this user is 502 and the group for the user is test. If we look at the group details we will see that a new group named test has been created with the GID of 502 (matching the new UID):
Well, If you are only interested in server based workflows you can simply file this as an interesting tidbit and continue surfing. (Might I recommend Crazy Apple Rumors?) However, if you need to share a folder between local users on a machine this is going to make your life tougher.
You see, for this scenario in Jaguar, you could be confident that all users shared the same primary group (staff). So as long as you set the group ownership of a folder to staff and maintained the correct permissions for it's contents you were golden. That is no longer the case. Now we have a unique group for each of our users, giving us something new to manage.
Like anything in OS X, there are a several of ways to attack this problem. In this case there is the not so good way, and the somewhat better way. Lets start with the not so good way:
We could (but I don't recommend) use SharePoints or NetInfo Manager to change the primary group of all of our users to staff, and then use chgroup -R staff /Users/userHome on each of their home directories.
This obliterates any added security that the new group membership model brings, and may have unintended consequences for users if they have modified any of the group ownership of their folders or if Apple changes anything in the future. Generally an icky solution I think.
An alternate solution relies on the fact that users can belong to multiple groups. All users have a primary group as we've seen, but can also have numerous secondary groups (I for instance am a member of the staff and admin groups). We can use this to our advantage.
To start we will need to create a new group. We could use NetInfo Manager to do this, or since SharePoints is already running we can us that (bias? what bias?).
Choose the Groups tab in SharePoints. Enter the name of the group you wish to create in the Group: text field (something catchy like: workgrp). Click on the Get Next GID button and click on the Add New Group button.
After creating the workgrp group we can associate it with users. Select the group in the Group list, then select the users in the workgroup in the Users list and click on the giant + button.
Now that our users have a common group, we need to modify the common directory used by the workgroup. For this example we're going to create a /Users/Workgroup folder.
sudo mkdir /Users/Workgroup sudo chown root /Users/Workgroup sudo chgrp workgrp /Users/Workgroup sudo chmod 775 /Users/Workgroup
We should now have a directory /Users/Workgroup/ with root as the owner, workgrp as the group. It should have read/write permissions for both the owner and the group and read only permissions for everyone else.
Now we can use either the umask, or cron tab solutions in the first article to maintain the permissions on items in this directory, with one exception...
Any files or folders created in (or copied into) the /Users/Workgroup directory will inherit its group membership, which is good. Unfortunately this is not the case if any item moved into it, those will maintain their original group membership. The possibility of a move occuring is not an issue in a server based environment (where everything is a copy) but on a local machine it is a distinct possibility.
The solution is simple, just educate your users to always work in the directory, or if not, then copy work into it and delete the original. If you are really paranoid you could write a cron task to periodically chgrp -R workgrp /Users/Workgroup/ so the group memberships are maintained as well.
I am hoping these articles will become obsolete at some point because Apple introduces a robust ACL (Access Control List) solution for OS X. Until then, I hope you find this information useful.
I am working on a new Workflow AppleScripting article (no, really!) so please stay tuned....
This is the second (long-time-in-coming) installment in our AppleScripting the Workflow series. In our previous article and its companion tutorial we looked at the power of AppleScript and it's basic syntax. This time we are going to look at the biggest task involved in workflow automation: Workflow analysis.
Workflow analysis is a fancy name for the process of identifying the tasks and the required results that make up your workflow. Workflow is the process you follow to produce your product. For our purposes that may be a website, catalogue, flyer, magazine or any other concrete product that you digitally generate.
So, to analyze a workflow we need to start somewhere. Top or bottom, you pick. We can either start at the top and work our way down or start at the bottom and work our way up. The point of the exercise is to identify our Projects, Phases, Steps, and Tasks.
At the top of our hierarchy is the Project. It may be to produce a catalogue, photo portrait or magazine ad. Whatever it is, it defines the end result of your process. You may produce several types of projects in your organization, so each will need to be identified with its own project workflow.
Depending on the complexity of your projects they will have at least one step, and possibly several. Some of the steps may be complex enough to require several tasks to complete them. Its is not really important how you organize the information you gather but rather that you gain a intimate understanding of:
Many projects are complex enough that it helps to organize our steps and tasks into phases - for instance our Catalogue may have Marketing input, Graphic design, Production, Proofing, Approval, Print as its phases.
Depending on the scope of your projects and the size of your organization this step may be difficult. If you are the chief cook and bottle-washer then you already perform all the tasks so gathering the information will be easy. However most organizations are more segmented than that, so information gathering may be more difficult.
It is key that you solicit feedback from the people in your organization who actually perform the tasks. We like to use a public wall, perhaps in a boardroom, that can get covered with sticky-notes. That way those involved can see and contribute to the big picture. Different colour notes can be used to define the tasks and their products, requirements, and dependencies.
We've found that sticky-notes work better than a whiteboard because it is easier to reorganize things when new interdependencies are identified. It's also a good idea to post a legend that defines the format of the information required on each note.
Once you have reached a consensus on the accuracy of your project wall, its time to start capturing that in a more durable form. There are many products out their that can help, but one of our favorites is the Omni Group's OmniGraffle.
Using OmniGraffle it is easy to create flow-charts and diagrams of our project workflows that makes the information easy to read and understand.
Once you've captured all of the information and understand all of tasks at play, its time to begin to identify likely candidates for automation. That is what we will look at in the next installment. So until then...
AppleScript is an indispensable tool for automating your Mac. It's been part of the Mac OS since System 7 and over the next few weeks we are going to examine AppleScript and how you would use it automate your workflow. We'll look at the AppleScript components, workflow analysis, candidate identification, and finally a real world-case study. This week we'll start by looking at the fundamentals of AppleScript and some resources for mastering the language.
NEWS FLASH: We've added a thin AppleScript Tutorial to give you a feel for the language. If this article makes you want to get your hands dirty give it a try.
According to Apple, "AppleScript is an English-like language used to write script files that automate the actions of the computer and the applications that run on it." You will hear the term "English-like" used quite regularly to describe the AppleScript language, so what exactly does that mean? Well, lets look at an example:
tell Application "Finder" open every file of desktop whose name begins with "openMe" end tell
Let's examine the contents of this script:
The first line and the last line define what is known as a Tell Block. Tell blocks are used to target the actions of a script. In this case all of the commands inside the tell block will be directed at the Finder.
Inside the the tell block, we can tell the Finder what we want it to do, and in this case, it is pretty clear what that will be. The script is going to ask the Finder to open every file [on the] desktop whose name begins with "openMe". This example should make it obvious why AppleScript is referred to as having an "English-like" syntax.
AppleScript is a rich language. It contains all of the things you would want in a programming language including variables, conditionals (if, else, then statements), looping (or iteration) and subroutines (handlers). Unlike macro languages that lock you into a prerecorded set of steps, AppleScripts are much richer. They can possess the smarts to make decisions based on user input, current conditions, or the results of previous steps.
AppleScript is built into Mac OS X, and contains a core set of commands and objects. It's power however, comes when used in conjunction with AppleScriptable applications. For instance the script example we looked at previously relies on the Finder to do all of the work. The Finder is a highly scriptable application and it is not alone. Quark Xpress, Adobe Indesign, BBEdit and Fetch are just a few of the many applications that have rich AppleScript Dictionaries
When a developer commits to adding AppleScript support to their applications, they are exposing an Object Model of the kinds of object they work with to your scripts. That means we can issue commands to the Finder like open every file of desktop whose name begins with "openMe" and know it will do what we want. Without a well implemented object model we would have to ask for all of the files on the desktop, and then iterate through each one checking to see if it's name matched our criteria. The Finder's object model and the whose clause in the script did all of that for us. Note: It must be said that not all AppleScript implementations are equal, some applications do it far better than others. So: "You're milage may vary, void where prohibited by law, for external use only and if swelling persists please consult a doctor."
If you've installed OS X on your Mac you have everything you need to start learning AppleScript. Go to /Applications/AppleScript/ and you will find the Script Editor application. This is a fine script editor to get you started (you'll find a list of others below). Start with one of the tutorials or books lited below and also be sure to check the sample scripts in the /Library/Scripts/ folder for plenty of examples.
Next week we will look at how to decide where to apply our new found AppleScript prowess as we dive into workflow analysis. Deciding where to automate is as important as how to automate, and that's what we'll examine next. So until then....
This is a thin tutorial that is designed to give you a feel for the AppleScript language by demonstrating it through a slew of samples. Each snippet builds on the previous ones and demonstrates a new concept or area of the language. Play along and have fun.
Note 1: If you haven't read through the first part of the Workflow AppleScripting article we suggest you do that now.
Note 2: AppleScript uses -- for single line comments and (* and *) for multiple line comments.
Note 3: AppleScript commands cannot span multiple lines. Because of html formating issues, long lines will occasionally get split into two or more. To show this, lines that start with ← are a continuation of the line above.
This script demonstrates the internal commands built into the AppleScript language (there are many more: display dialog, chose folder, chose file, etc.) Copy this into your Script Editor and click Run the script should play your system alert sound.
--beep script beep
Scripting the Finder is a common task, this first script demonstrates the use of a Tell Block to focus the action of the script. It also demonstrates the use of objects and commands provided by a scriptable application. (Exercise: Use the Script Editor's->File->Open Dictionary... command to look at the Finder's AppleScript Dictionary)
--finder tell application "Finder" open disk "Macintosh HD" end tell
This second script does the same as the first but does it better. The first script expects to find a disk named "Macintosh HD" on your machine, this script uses one of the standard locations (startup disk) instead. This makes the script far more robust and portable. A good thing all around.
--better tell application "Finder" open startup disk end tell
Using the make new command to create a folder on the desktop (again, notice the use of a standard location desktop)
--create folder tell application "Finder" make new folder at desktop end tell
Objects (like files, folders or applications) have properties. This script shows how to create a folder with a given name.
--create a folder with a name
tell application "Finder"
make new folder at desktop with properties
← {name:"Hello there I'm a new folder"}
end tell
AppleScript has several types of variables. This script snippet shows their usage.
--variables set theWord to "Hello" -- setting a text variable's value say theWord -- accessing the variable set theNumber to 5 -- setting a number variable's value set theNumber to theNumber + 1 -- doing some math
AppleScript handles most coercion (conversion from one data type to another) for you automagicaly.
--variables set theWord to "1" --setting a text value set theWord to theWord + 2 --now its a number: result is 3 set theWord to theWord & " Monkeys" -- now its back to text: result is "3 Monkeys" set theWord to theNumber as string - forced coercion
In addition to the simple variables above AppleScript supports two more complex data structures: the lists and records. Lists are lists of objects, records are made up of name:object pairs
set theList to {1, 2, 3, 4}
set theRecord to {firstName:"David", lastName:"LeBer"}
Lists can be accessed by numeric or relative position
set theList to {1, 2, 3, 4}
set theNumber to item 1 of theList
set theNumber to the last item of theList
Records are accessed by name
set theRecord to {firstName:"David", lastName:"LeBer"}
set theLastName to lastName of theRecord
It is important when working with AppleScript that you unambiguously identify the object you want to act upon. AppleScript will not be happy with you if you don't. You need to know the object hierarchy for whatever object your are working with. For example, TextEdit has the following hierarchy TextEdit->Document->Paragraph->Word. And this is how you identify the 4th word in the second paragraph of the frontmost document in TextEdit:
--object hierarchy tell application "TextEdit" set theWord to word 4 of paragraph 2 of document 1 end tell
Although out of the box AppleScript doesn't come with rich GUI tools (you'll have to use AppleScript Studio for that) it does possess some ways of interacting with the user. display dialog is one of the most common.
--giving feedback display dialog "Hello World" --you just knew there would be a Hello World example didn't you?
Decisions, decisions. AppleScript uses if--then--else conditionals to make decisions. Notice the important end if statement.
--conditional set theColour to "blue" if theColour is equal to "blue" then say "I'm feeling sad" else if theColour is equal to "red" then say "I'm feeling mad" end if
This script demonstrates using the display dialog command along with some of its additional properties and an if statement acting on the special result variable (that always contains the result of the last action).
--getting feedback 2
display dialog "Give me an answer" default answer "" buttons
← {"OK", "Not OK", "Not even close to OK"}
if text returned of result is "" then
display dialog "you weren't listening"
end if
The choose folder (and its cousin the choose file) command can be used to prompt the user to pick a folder (or file). In this example we explicitly assign the result of the choose folder command to a variable (theFolder).
This script also demonstrates the strength of the Finder's Object Model by allowing us to filter all of the object in the chosen folder by using the whose command.
--getting feedback and finder tell application "Finder" set theFolder to choose folder "Select a Folder please" open (every file of the theFolder whose ← name starts with "New") end tell
Iteration (or looping) is a very powerful part of any programming language. AppleScript has several different types of repeat statements. This example uses the repeat with aVariable in aList form.
--iteration or looping
set theColourList to {"red", "white", "blue"}
repeat with aColour in theColourList
say aColour
end repeat
These next scripts create a new document in Adobe Indesign. The first is very simple, it asks Indesign to create a new document with its default preferences. Notice the tell block.
--scripting an Application tell application "InDesign 2.0.2" set myDocument to make new document end tell
After studying the AppleScript Dictionary for Indesign (Script Editor->File->Open Dictionary...) we discover that the document dimensions are stored in the document preferences of the document So we can modify our script to create an new document with a specific size (2 by 3 inches).
--scripting an Application tell application "InDesign 2.0.2" set myDocument to make new document tell document preferences of myDocument set page width to 3 set page height to 2 end tell end tell
Unfortunately the new document still has a the default margins. Which in our case are .5 inches. They are far to large for our new document size. Again after studying the Indesign Dictionary we discover that the margin values in the document preferences are read only. We need to adjust them in the margin preferences and they need to be set before we create the document
--scripting an Application tell application "InDesign 2.0.2" tell margin preferences set margin top to 0.1 set margin bottom to 0.1 set margin left to 0.1 set margin right to 0.1 end tell set myDocument to make new document tell document preferences of myDocument set page width to 3 set page height to 2 end tell end tell
OK, we're getting really close now. The only problem left is that the next user who uses Indesign after our script has run will find that the margins are not where they left them. Probably not a way to make friends. This is a common problem as you develop more complex scripts and it is relatively easy to fix. We will cache the values we need to change before we start, and restore them afterwards.
--scripting an Application tell application "InDesign 2.0.2" tell margin preferences set priorTop to margin top set priorBottom to margin bottom set priorLeft to margin left set priorRight to margin right set margin top to 0.1 set margin bottom to 0.1 set margin left to 0.1 set margin right to 0.1 end tell set myDocument to make new document tell document preferences of myDocument set page width to 3 set page height to 2 end tell tell margin preferences set margin top to priorTop set margin bottom to priorBottom set margin left to priorLeft set margin right to priorRight end tell end tell
OS X brings with it a very rich command line environment. AppleScript has evolved to be able to take advantage of it as this simple script demonstrates Note: Most unix shell commands do not understand Mac OS resource forks! Be careful what you choose to manipulate with them.
--do shell script do shell script "mv ~/Desktop/test.txt ~/Documents/"
OS X allows you to create very powerful watched folders by attaching AppleScripts to folders via folder actions
Copy this script into Script Editor and save it as a Compiled Script into /Library/Scripts/Folder Action Scripts/. Control click on a folder and select Configure Folder Actions... and make sure the checkbox beside Enable Folder Actions is checked
Control click on a folder and select Attach Folder Action... and navigate to /Library/Scripts/Folder Action Scripts/ and pick the script you saved.
Test by dragging something into the folder. You should hear your system alert sound when the move is finished.
--folder action on adding folder items to thisFolder after receiving addedItems beep end adding folder items to
Although not comprehensive, I think this gives a fairly good overview of the basic syntax of AppleScript. Be sure to check the resource links in the Workflow AppleScripting article for more detail. Study assignemnt: investigate the three ways to reference a file in AppleScript (hint: posix, path, alias).
Get notified when there are new articles.
We're not the only ones with bandwidth and a need to share. Here are some of our favorite technical resources on the web.
We've been lifetime subscribers to MDJ for, like, ever. Insightful, biting and timely. Well worth the cash.
Need software? Versiontracker will help you find it.
High on news, low on press releases. I like the layout too.
Great layout, like the writing style. Gruber writes for MacJournals too.
Need to learn about the deeper levels of Mac OS X? Mac OS X Labs can help.
Heterogeneous, Heterogeneous, Heterogeneous!