Rotate NSImage in an NSImageView

I didn’t want to half-implement the new tab feature in Slider – I want people to have almost as much control as if they were coding the site by hand.

This has meant I’ve had to rethink Slider’s UI many times, and I’ve had (re: couldn’t face shipping without) to implement many small features that I feel are required, from a usability standpoint.

When I decided that the user may choose to place tabs in 12 different positions, I needed to consider how this would effect the arrows and sliding animations. I came to the conclusion that if the user elects to place the tabs on the top or bottom, the arrows will be on the left and right, with the animation going right/left. If the user chooses to place tabs on the left or right, the arrows will be positioned on the top and bottom, with the animation going up/down.

Pictures make it easier:

tab_position

tab_position_1

As soon as I implemented this I realised how much of a pain it would be to copy out the arrow images, rotate them in an external program, then copy them back in. So I put some buttons next to the arrow image wells that allow the user to rotate the arrows within Slider itself.

Here is the code I used to rotate the images (thanks to Jerry Krinock and Steve Christensen and for posting this solution to the Cocoa-dev list):

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#import <Cocoa/Cocoa.h>
 
@interface NSImage (Transform)
 
 
/*!
 @brief    Rotates an image around its center by a given
 angle in degrees and returns the new image.
 
 @details  The width and height of the returned image are,
 respectively, the height and width of the receiver.
 
 I have not yet tested this with a non-square image.
 
authors: Jerry Krinock and Steve Christensen
 */
- (NSImage*)imageRotatedByDegrees:(CGFloat)degrees ;
 
@end
 
#import "NSImage+Transform.h"
 
@implementation NSImage (Transform)
 
 
- (NSImage*)imageRotatedByDegrees:(CGFloat)degrees {
 
	// calculate the bounds for the rotated image
    NSRect imageBounds = {NSZeroPoint, [self size]};
 
	NSBezierPath* boundsPath = [NSBezierPath bezierPathWithRect:imageBounds];
 
    NSAffineTransform* transform = [NSAffineTransform transform];
 
    [transform rotateByDegrees:degrees];
    [boundsPath transformUsingAffineTransform:transform];
 
    NSRect rotatedBounds = {NSZeroPoint, [boundsPath bounds].size};
 
	NSImage* rotatedImage = [[NSImage alloc] initWithSize:rotatedBounds.size];
 
    // center the image within the rotated bounds
 
	imageBounds.origin.x = NSMidX(rotatedBounds) - (NSWidth (imageBounds) / 2); imageBounds.origin.y = NSMidY(rotatedBounds) - (NSHeight (imageBounds) / 2);
 
    // set up the rotation transform
    transform = [NSAffineTransform transform];
 
	[transform translateXBy:+(NSWidth(rotatedBounds) / 2) yBy:+ (NSHeight(rotatedBounds) / 2)];
 
    [transform rotateByDegrees:degrees];
 
	[transform translateXBy:-(NSWidth(rotatedBounds) / 2) yBy:- (NSHeight(rotatedBounds) / 2)];
 
    // draw the original image, rotated, into the new image
    [rotatedImage lockFocus];
    [transform concat];
 
	[self drawInRect:imageBounds fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0] ;
 
    [rotatedImage unlockFocus];
 
    return [rotatedImage autorelease];	
 
}
@end

I had to change (from/to):

NSImage* rotatedImage = [[[NSImage alloc] initWithSize:rotatedBounds.size] autorelease];
 NSImage* rotatedImage = [[NSImage alloc] initWithSize:rotatedBounds.size];
return rotatedImage;
return [rotatedImage autorelease];
[transform set];
[transform concat];

For it to work flawlessly with Slider.

Thanks guys!

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Drag and Drop Reordering of NSTableView’s Rows

I just had to re-implement drag-and-drop-reordering of Slider cells. I was lucky enough to find my post on the Cocoa-dev list – I just had to copy-paste the code.

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
41
42
43
44
45
46
47
48
49
50
51
52
#define MyPrivateTableViewDataType @"NSMutableDictionary"
 
- (void)awakeFromNib {
[table registerForDraggedTypes:[NSArray arrayWithObject:MyPrivateTableViewDataType]];
}
 
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexestoPasteboard:(NSPasteboard*)pboard{
 // Copy the row numbers to the pasteboard.
 NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
 
[pboard declareTypes:[NSArray arrayWithObject:MyPrivateTableViewDataType] owner:controller];
 
 [pboard setData:data forType:MyPrivateTableViewDataType];
 return YES;
}
 
- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op{
 // Add code here to validate the drop
 NSLog(@"validate Drop");
 return NSDragOperationEvery;
}
 
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info row:(int)to dropOperation:(NSTableViewDropOperation)operation{
 
//this is the code that handles dnd ordering - my table doesn't need to accept drops from outside! Hooray!
 NSPasteboard* pboard = [info draggingPasteboard];
 NSData* rowData = [pboard dataForType:MyPrivateTableViewDataType];
 
NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData];
 
 int from = [rowIndexes firstIndex];
 
NSMutableDictionary *traveller = [[controller arrangedObjects] objectAtIndex:from];
 
 [traveller retain];
   int length = [[controller arrangedObjects] count];
   NSMutableArray *replacement = [NSMutableArray new];
 
   int i;
 for (i = 0; i <= length; i++){
       if(i == to){
         if(from > to){
         [controller insertObject:traveller atArrangedObjectIndex:to];
         [controller removeObjectAtArrangedObjectIndex:from+1];
         }
         else{
             [controller insertObject:traveller atArrangedObjectIndex:to];
             [controller removeObjectAtArrangedObjectIndex:from];
         }
     }
   }
}

Posting replies to the lists helps more than just the receiver – keep in mind that source code can be lost. Time is saved when tedious sections can be copy-pasted in during rewriting!

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Slider Update – Tab Feature Coming Together

As I finally reached a programming goal I had set – the tab hover javascript was working fairly well – I realised that users would want the option of positioning the tabs in the middle or corners of Slider’s four sides. Initially I had assumed it would be OK for users to have tabs positioned only in the center of either of the four sides.

Great, I thought. This shouldn’t be too bad – just increase the number of radio buttons available and position the tabs accordingly.

Four hours later, I “achieve” a build of Slider that exports without the dreaded Uncaught Exception 10.

None of the Javascript works, turns out that was where the final erroneous lines were. Not in the 6 other class files I combed through. Duh.

The radio buttons for selecting tab position changed from:

Ugh, these are so yesterday

Ugh, these are so yesterday

to:

Positioning Radios as they should be.

Positioning Radios as they should be.

Here is an example of the type of thing you’ll be able to do with Slider, when the rewrite is finally done:

It's getting there!

It's getting there!

Finally, I feel like the hours I’ve spent mashing my hands on my keyboard are starting to show results.

Not working, releasable results, but results all the same.

Still not working: arrows, timer, content (!), table row repositioning, titles, text shadow, effects (or any sliding at all, come to think of it), cell links.

I have another project that I will soon have to devote serious time to, but I’ll continue plugging away at Slider, I’m excited about the new version!

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

How to Prevent Noisy NSBitmapImageReps

Before

Images generated for Slider’s background and tabs contained a lot of noise – random ugly pixels throughout the images.

I was unsure why this was happening, so I asked the Cocoa-Dev mailing list.

Rob Keniger kindly replied with the following solution:

“An NSBitmapImageRep is not guaranteed to be empty when you create it and in general it’s just full of random bits. You need to clear it explicitly if you are drawing non-opaque content”

He included the following snippet that solved my problem nicely:

1
2
3
4
5
6
7
- (void) clearBitmapImageRep:(NSBitmapImageRep *)bitmap {
   unsigned char* bitmapData = [bitmap bitmapData];
   if (bitmapData != NULL)
   {
       bzero(bitmapData, [bitmap bytesPerRow] * [bitmap pixelsHigh]);
   }
}

I call it as soon as I’ve created a new NSBitmapImageRep, like so:

1
2
3
4
5
NSBitmapImageRep  *theImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:imageSize.width pixelsHigh:imageSize.height
                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
                            colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:0 bitsPerPixel:0];
 
[self clearBitmapImageRep:theImage];

This results in noise-free images.

After

Thanks Rob!










Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

The Mutations of Slider’s Tab Option Pane

During the past week, I’ve been working on Slider’s new Tab option pane.

I took screen shots before making changes to the layout.

Here they are:

Tabs Iteration #1

Tabs Iteration #2

Tabs Iteration #3

Tabs Iteration #4

I hope I’m not tempted to change things around again… I really need to get the backend going now!

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Accessing Primitives From NSDictionarys in Cocoa…

This “gotcha” had me scratching my head for a little while.

Edit: this “gotcha” stemmed more from my inexperience with Cocoa than anything else. NSDictionaries store only objects, one shouldn’t try to insert int/float/bool etc into one. Instead use NSNumber for these values. Duh.

When storing various NSObjects in an NSMutableDictionary, I assumed one would access (in this case the object being stored is an NSColor) them like:

1
[[dictionary objectForKey:idle_TabBorderAllColourKey] color]

I also assumed that one would access “int”, “float” and other primitives like:

1
[dictionary objectForKey:idle_TabBorderAllWidthKey]

After writing, compiling and attempting run my code, however, I discovered that I was wrong.

It should be done like so:

1
[dictionary objectForKey:idle_TabBorderAllColourKey]

For NSObjects, and:

1
[[dictionary objectForKey:idle_TabBorderAllWidthKey] intValue]

for primitives.

Cocoa noob learns more about Cocoa with each passing minute!

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Slider Progress: Slider Styling Option Pane

During the great rewrite, I’ve been trying to focus on one section at a time. The first section to come under attack was “everything pertaining to the styling of Slider that is not Arrow-related”. After much mucking-about I decided to combine the Border, Rounded Corner, Background Image, Gradient and Background Colour onto one Pane.

I don’t know why I didn’t do this before, it is obvious and really the only logical way to present these controls – enabling some may disable others, it was confusing and clumsy when they were on different tabs.

Now users will be able to see what is being enabled/disabled without swapping between different tabs.

Here is a screenshot of the option page as it is currently:

Slider style configuration laid bare: Click to view

Slider style configuration laid bare: Click to view

This is about the 10th layout redesign, I’m quite sure there will be no major changes from now on.

Still a long way to go, but progress *is* being made.

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

Comments (1)Trackback

Return an NSMutableString as NSString Avoiding “Uncaught Error 11″ with Cocoa

Another stumbling block on the road to Slider completion was this:

NSUncaughtSystemExceptionException — Uncaught system exception: signal 11

This vague and unhelpful error message (in this case) was caused by my trying to return an NSMutableString in place of an NSString:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (NSString *)generateHTML{
 
	NSMutableString *html = [NSMutableString string];
 
	@try{
 
	//Begin wrapper
	[html appendString:[NSString stringWithFormat:@"<div id=\"slider%@\" style=\"height:%ipx;width:%ipx;position:relative;z-index:0;margin-left:%ipx;%@\">",
						UID, (int)portalSize.height, (int)portalSize.width, leftMargin, [self generateBGCSS]]];
 
	//Begin other content
	[html appendString:[self someYetToBeWrittenFunction]];
 
	//Close wrapper
	[html appendString:@"</div>"];
 
	}
	@catch (NSException *e) {
		NSLog(@"generateHTML: %@", e);
	}
	return html;
}

Googling resulted in this solution, from the Cocoa Dev Archives (which have saved me from suicide more times than I can count).

The successful code is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (NSString *)generateHTML{
 
	NSMutableString *html = [NSMutableString string];
 
	@try{
 
	//Begin wrapper
	[html appendString:[NSString stringWithFormat:@"<div id=\"slider%@\" style=\"height:%ipx;width:%ipx;position:relative;z-index:0;margin-left:%ipx;%@\">",
						UID, (int)portalSize.height, (int)portalSize.width, leftMargin, [self generateBGCSS]]];
 
	//Begin other content
	[html appendString:[self someYetToBeWrittenFunction]];
 
	//Close wrapper
	[html appendString:@"</div>"];
 
	}
	@catch (NSException *e) {
		NSLog(@"generateHTML: %@", e);
	}
	return [NSString stringWithString:html];
}

This should have been obvious, I regret the shame I bring on my family.

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Lesson #1: InvalidDnDOperationException Ruins Sanity

A friend with whom I am currently working on a programming contract called me today to tell me that the latest build wasn’t working. At all.

This irritated and confused me, as I hadn’t worked on that particular project for a week or so, and couldn’t remember having any issues with it.

I booted into Windows (I use Linux mostly), downloaded Eclipse and imported the project.

Sure enough:

java.awt.dnd.InvalidDnDOperationException: Drag and drop in progress
	at sun.awt.dnd.SunDragSourceContextPeer.setDragDropInProgress(Unknown Source)
	at java.awt.dnd.DragSource.startDrag(Unknown Source)

This error had attacked me before, but a quick investigation revealed that the issue was different this time. Joyous news.

I poked about in the (too many) classes that make up this project, but discovered nothing of use.

After some fruitless Googling, it occurred to me that during the great Refactoring Storm of August, I may have changed some of the DragGestureRecognizers’ DnDConstants to ACTION_COPY instead of ACTION_COPY_OR_MOVE.

A short, smaller storm of refactoring esued, during which all ACTION_COPY_OR_MOVE DnDConstants were changed to ACTION_COPY.

Why was this an issue at all? I was using evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); to accept the drops, which I guess I must have assumed would accept an ACTION_COPY drop. Seemed to in Ubuntu, anyway. Maybe the Windows JRE is more strict (re: mental)?

Why this issue hid itself until now, I don’t know. Perhaps Ubuntu just likes me more.

Lesson: test software on the target system BEFORE giving it to your team-members.

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Tile an NSImage Within an NSBezierPath

Another snippet I had to write during the great Slider rebuild.

I needed to be able to take an NSImage supplied by the user and tile it within an NSBezierPath defined by more user-set values.

The below example uses a rounded rectangle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NSBitmapImageRep *theImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:imageSize.width pixelsHigh:imageSize.height
 bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
 colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:0 bitsPerPixel:0];
 
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:theImage]];
 
//bgImage can be any NSImage
NSColor *c = [NSColor colorWithPatternImage:bgImage];
[c set];
[path appendBezierPathWithRoundedRect:NSMakeRect(0, 0, imageSize.width, imageSize.height) xRadius:10.0 yRadius:10.0];
[path fill];
 
[NSGraphicsContext restoreGraphicsState];

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Fill a Path With A Scaled Image, Ignoring Image Proportions

While continuing to rebuild Slider’s background image features, I was forced to rewrite the code that scales an image up/down to Slider’s portal size, ignoring the image’s original proportions. The image also had to be painted within a path, as Slider may have differently shaped corners compared to the image to be scaled.

The below code accomplishes the scaling and painting of the image.

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
//theImage = the resulting image, that will have a scaled (proportions ignored) version of "bgImage" painted onto it
NSBitmapImageRep *theImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:imageSize.width pixelsHigh:imageSize.height
 bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
 colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:0 bitsPerPixel:0];
 
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:theImage]];
 
NSImage bg = [[NSImage alloc] initWithSize: imageSize];
 
//bgImage can be any NSImage
NSSize originalSize = [bgImage size];
[bg lockFocus];
[bgImage drawInRect: NSMakeRect(0, 0, imageSize.width, imageSize.height) 
fromRect: NSMakeRect(0, 0, originalSize.width, originalSize.height) 
operation: NSCompositeSourceOver fraction: 1.0];
 
[bg unlockFocus];
 
NSColor *c = [NSColor colorWithPatternImage:bg];
[c set];
[path appendBezierPathWithRoundedRect:NSMakeRect(0, 0, imageSize.width, imageSize.height) xRadius:10.0 yRadius:10.0];
[aPath fill];
 
[NSGraphicsContext restoreGraphicsState];

“path” could be any NSBezierPath.

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Write Your Own Listener Interface (you know you want to)

I’ve used Listeners a lot in Java. The standard listeners have always been sufficiently flexible for my needs.

Until today.

This tutorial was all I needed to quickly implement my listener: Listeners in Java

It’s clear, succinct and comprehensive. When you need to implement your custom listener interface, check it out.

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Java: repaint() Waking Nightmare

You know those times, while coding, when it feels like you’ve drifted into a horrible nightmare where nothing you do works?

This happened to me today.

I wanted the little app I’m making to display a green tick or a red cross depending on whether the user had entered valid credentials. Simple? Yeah. Everything went really well, I extended a JPanel to handle the tick/cross display area (tickCross), got the POST code going, strung together the rest of the GUI elements.

The problem was that tickCross.repaint() wasn’t reliably repainting. about 30% of the time the image wouldn’t be painted. The method was being called, but nothing was happening. I tried Google, re-read the related Sun Java docs and was reminded that: “repaint() does not actually paint. It calls the peer repaint which enqueues a request in some platform-dependent way inside the native GUI for a repaint.” – MindProd. Great. My OS was deciding when I was allowed to draw.

Somewhat more irritated, I then had to resort to every programmer’s backup tool: trial and error.

Thanks Rob (we make websites), for laughing at this post and emailing me the following:

tickCross.repaint();
tickCross.setVisible(false);
tickCross.setVisible(true);

?! How about:

Invalidate();

I swear I tried that, honest!


After much frustration, I struck a solution. It’s not elegant, I’d even say it’s hacksih, but it works.

1
2
3
tickCross.repaint();
tickCross.setVisible(false);
tickCross.setVisible(true);

Cross or tick when I damn well want them, every time.

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Auto-Notification of Broken Links, Fantastic Pre-Written Anti-Spam .htaccess File!

Recently I’ve been learning a bit about .htaccess, redirecting visitors who come via now-broken links.

To help with this, I inserted a little mailer script into my error 404 page that sends me an email containing the referring URI, the URI the visitor was trying to reach and various other tidbits of information. It works well, allowing me to quickly fix broken links that I would otherwise be unaware of.

It also writes the beginning of a rewrite rule, as if I wasn’t lazy enough.

Here it is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<? php 
	//mail me about this error...
	$to = "mike(@)pagesofinterest(.)net";
	$base = "http://pagesofinterest.net";
 
	$referrer = $_SERVER['HTTP_REFERER'];
	$requested_page = $_SERVER['REQUEST_URI'];
	$agent = $_SERVER['HTTP_USER_AGENT'];
	$ip = $_SERVER['REMOTE_ADDR'];
	$subject = "Error 404 - $requested_page";
	$body = "The page: $requested_page, referred to by $referrer does not exist.  Fix this or add a rewrite.\n\nAgent: $agent\n\n";
	$body .= "IP: $ip \n\n";
	mail($to, $subject, $body, $headers);
 
	//append to file:
	$redirect = "Redirect 301 $requested_page http://pagesofinterest.net/\n";
	$filename = "redirects.txt";
	$fh = fopen($filename, 'a');
	fwrite($fh, $redirect);
	fclose($fh);
?>

Use:

sort redirects.txt | uniq > unique_redirects.txt

In a terminal to get a smaller file containing only unique broken links.

One problem was the sheer volume of messages I was getting. I did some research, and it seems this site has become the target of spammers. After a bit of googling on how to discourage this, I came across the best thing a newb could hope for: a perfect, commented example. Aaron Logan has graciously made his .htaccess file available to the world. It handles bad user agents, known bad IP’s, and keywords in referrer URI’s. It is gold.

You can get your own copy of it here: best anti spam .htaccess file.

Thanks Aaron!

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

No commentsTrackback

Find and Replace Text Within Multiple Files in Linux – Avoid RSI

After updating 100+ pages manually, I realized that I had neglected to add “index.php” to the end of certain links. Usually this would be fine, but the links in question are opened in Shadowbox, which will fail on pretty, “index.php”-less links.

I was able to fix this on the server in 5 seconds with find+perl:

find . -name 'FILE_NAME.EXTENSION' | xargs perl -pi -e 's/FIND/REPLACE/g'

Where FILE_NAME is the name of the files to be searched in (can be ‘*’) and EXTENSION is the filetype (can be ‘*’). FIND is the text to be searched, and REPLACE is a replacement string.

Phew!

Like this post? Move it on along with:

email Email | delicious delicious | digg Digg | Tweet this post Tweet | reddit Reddit | newsvine Newsvine | furl Furl | google Google | StumbleUpon Stumble | Hao Hao HaoHao


Trackback:

Comments: 0 | Comments Feed

Scroll to post title

Comments (1)Trackback