The newLISP on Rockets Blog

Currently running newLISP on Rockets version: 0.48

Avatar uploads are working!

Post #: 68
Date: 2012-11-19 20:52:42.000
Author: Rocket Man

After a fair bit of fiddling, I've gotten avatar uploads working on the newLISP on Rockets blog. Now you can upload your own image.

The code to display a file upload form looks like this:

(displayln "<p>Upload new avatar (all avatars scaled to 64x64 pixels): <form name='FileUpload' action='rockets-avatarupload.lsp' method='POST' enctype='multipart/form-data'><input type='file' id='uploadName' name='uploaded_data' onChange='this.form.textname.value = this.value'><input type='hidden' name='textname'><input type='submit' value='Upload' name='submit'></form>")

The form variables are put into the ($POST) tree, along with a variable called "filename" that holds the file name, and "binary-data" that holds the file itself.

To retrieve these from ($POST), just use the following code:

(set 'file-name ($POST "filename"))
(set 'file-binary ($POST "binary-data"))

Then to save the file , I use the following command:

(write-file (string "images/avatars/" file-name) file-binary)

One bit of warning: you will need to set the ownership permissions on the directory where you are saving these files to something that the Apache user has write access to. In Ubuntu this was something like:

sudo chown www-data:www-data images/avatars

And that's it! The avatar images are not scaled when they are uploaded, but they are displayed with fixed dimensions of 64x64 pixels. This means if you upload a larger image it will look tiny. In the future I will add a module that automatically resizes files to a dimension of your choice, but for now, try to keep avatars to no larger than 100x100.

Comments (2)

Views: 2100

The feeling of triumph when fixing a stubborn bug...

Post #: 67
Date: 2012-11-13 16:26:48.000
Author: Rocket Man

It's not quite like winning an Olympic Gold Medal or anything, but it's a nice feeling.

I was having a very intermittent problem with long posts being truncated. Most posts are short enough that they didn't encounter this problem, but longer posts with code would sometimes just cut off randomly. The weird thing is that if I went back and refreshed the page they would go through. It wasn't predictable at all. Those kind of bugs are the most annoying!

Basically I had some old code that followed a similar technique that Dragonfly used:

(when (> (peek (device)) 0)
(if (and (read (device) post-buffer $MAX_POST_LENGTH) post-buffer) ; grab all post data, put it in variable 'post-buffer'
(parse-get-or-post post-buffer $POST)

The idea is that you're reading from (device) into a variable called post-buffer. Hard to figure out what's going wrong in there!

Not sure how to fix this, I went and looked at the code in Artful Code's A Better newLISP Web Library:

The author had taken a slightly different approach, which I adapted into my framework:

(let ((buffer "") (post-buffer ""))
(unless (zero? (peek (device)))
(while (read (device) buffer $MAX_POST_LENGTH)
(write post-buffer buffer))
(parse-get-or-post post-buffer $POST)))

Basically, instead of just reading the (device) once, it CONTINUES to read the (device) and copies whatever it finds into a temporary variable "buffer" that gets appended to the variable "post-buffer" until there is nothing left to read.

It's nice when things get fixed like this. It makes me very happy.

Comments (8)

Views: 2376

MVC code patterns, templates, and complexity

Post #: 65
Date: 2012-11-08 15:15:49.000
Author: Rocket Man

Today I implemented an entirely different way to view posts and comments, the forum view. When you view a blog post, the same post also exists in the forums. In fact, you can click on the "View post in forums" button to toggle the views.

A lot of people would tell you to write this kind of thing using the MVC (model-view-controller) model, or at least use a framework that has a templating system so that you can view the same data differently.

This is a completely valid perspective and I'm not going to argue otherwise. MVC is a well-established design methodology and it works pretty well for a lot of different applications. Templates seem like they could be really useful.

I didn't implement the forum view this way, however. I just had a toggle variable called (forum-view-post) and checked to see if it was true. If it was, then I executed a different code block. The real danger here is code duplication, which is something we always want to avoid.

The thing is, implementing this very different view took only ten lines of code.

(displayln "<h3>" (list-post-data 3) "</h3>")
(set 'header-list '("Author" "Message"))
(set 'post-data (list (string "<img src='images/avatars/" (author-avatar (list-post-data 1)) "'><br>" (author-name (list-post-data 1))) (format-for-web (list-post-data 4))))
(set 'PostId (int (list-post-data 0)))
(set 'post-data (list post-data)) ; okay these two lines of code are duplicated... I can live with it for now
(set 'post-comments (get-record "Comments" PostId))
(if post-comments (begin
(dolist (p post-comments)
(push (list (string "<img src='images/avatars/" (author-avatar (p 2)) "'><br>"(author-name (p 2))) (format-for-web (p 5))) post-data -1)) ; add each comment to the thread
(display-table header-list post-data "striped")

Okay, so two lines of code are duplicated in the alternate (blog) view, which I had already written. If it was much more than this, maybe I'd write a function and move it outside the block so that I didn't have duplicate code. Obviously I don't want to have to change things multiple times every time I change the view. But these two lines only extract the comment data from the database, so I wouldn't be changing these anyway.

Now, let's say I wrote this using a template system, or with MVC, or both. It would be a lot more than ten lines of code. I would have to learn another language (the templating language) and I would have to write a bunch of classes and methods and make sure they all work properly together, maybe writing a bunch of unit tests to ensure I don't break anything if I change the contracts between the model, view, and controller. More code equals more opportunities for bugs. In fact, the number of lines of code is the only aspect of development that has a strong correlation with the number of bugs. Everything else is too hard to measure. Fewer lines of code also take less time to write, in general. So all else being equal, wouldn't you want to have fewer lines of code to do the same thing?

As far as templates go, they make a lot of sense in a big company where there is a whole department doing user interface design and another department doing coding. The designers can learn the templating language and HTML, and not have to learn anything else. For my personal development, I am both the interface designer and the developer.

The newLISP code is my templating language. I don't want to have to learn another language if I can just keep everything together. Overall, the arguments for using MVC and templating work better for larger projects with lots of people. newLISP on Rockets is optimized for very small development teams, including projects where the team is only one person. In these cases, simplicity almost always wins out over complexity.

Comments (9)

Views: 2143

Linking a blog to a forum...

Post #: 59
Date: 2012-11-06 15:17:32.000
Author: Rocket Man

Blogs and forums are two different things, but they don't have to be.

I'm adding a forum to the Rockets blog, where any registered user will be able to post on any topic. Each new blog post will appear on the forum, and any comments added in the forum will be reflected in the blog itself. But the inverse is not true-- any new forum post will not appear on the blog. This allows other users to start discussions and interact with each other without interrupting the blog itself.

The way I do this is to add a new column called "PostType" that can be either "Blog post" or "Forum post". The former will be retrieved by the main page, while the latter is retrieved by the forum page.

I'm also using my new (display-table) function to display the forum posts.

Comments (10)

Views: 1917

Tables and Avatars...

Post #: 53
Date: 2012-11-02 23:27:05.000
Author: Rocket Man

A couple of additions today...

First, a Table function that prints some nicely-formatted HTML tables.

(display-table list-of-headers nested-list-of-data "optional form styling")

striped - alternates rows in grey
bordered - adds borders and rounded corners to the table
hover - enables hover state on table rows when mousing over
condensed - more condensed style of table

This is a neat way of displaying tabular data in a pretty way, just by passing in lists.

The other thing is that I've changed the formatting of comments to include user avatars to the left of each comment. Unfortunately I didn't have time to get avatar uploading working. That will have to wait for next week!

Comments (4)

Views: 1803

I've got a Rocket!

Post #: 52
Date: 2012-11-01 21:42:04.000
Author: Rocket Man

Some sites have a mascot. Okay, we have a rocket, let's call him Rocketty. Done.

Now all we need is a theme song.

This is another UBB code enabling the user to embed YouTube videos. Just use the tags [ youtube ] and [ /youtube ] (without the spaces) and in between, put the alphanumeric string of that video (eg: rJppnG1tflU)

Comments (1)

Views: 2145


Post #: 51
Date: 2012-11-01 17:17:39.000
Author: Rocket Man

So, now we have the final U in CRUD done, and I can finally edit posts. YOU can't edit comments yet, but that's coming soon, probably tomorrow.

That's fun. EDITING ROCKS! It's also a really neat macro that makes it pretty easy to do SQL update statements.

Here's another neat thing: Basically, I use the exact same statement to create and update a post:

(update-record "Posts" Id PosterId PostDate PostSubject PostContent) ; It's the U in CRUD!

(create-record "Posts" Id PosterId PostDate PostSubject PostContent) ; It's the C in CRUD!

There's a lot of stuff going on behind the scenes to translate the above into two very different SQL statements. Basically the Id in the first is to CHECK for Id's value, and in the second statement it is to SET Id to that value. I like the symmetry,

Comments (19)

Views: 2552

Parameterizing SQL queries

Post #: 50
Date: 2012-10-31 00:37:48.000
Author: Rocket Man

Sometimes SQL injection comes in tricky forms. Often attackers will add on extra junk in the URL to try and confuse SQL into doing something it wasn't intended to do. For example, if the URL for page 2 is:

an attacker might try something like OR 1=1

to try and break the code or inject some other SQL into the query by adding these commands with spaces.

I've added a function called (force-parameters) that makes it easy to prevent this. For example, to get the page number from the URL, I used to call ($GET "p"), which in this case would return "2 OR 1=1".

Now, I use the code:

(set 'current-page (force-parameters 1 ($GET "p"))) ; we only need the first part, ignore anything else

which takes only the first parameter from the URL. You can add as much extra junk after it, but it will only take the "2".

Views: 1715

Testing UBB code

Post #: 49
Date: 2012-10-29 23:21:47.000
Author: Rocket Man

Just a little bold and italics and underlines

and some code samples:

(format-for-web str-input-text)

The above code now translates these UBB codes into HTML. Actual HTML is converted to its literal equivalent to avoid cross-site scripting issues.

Comments (3)

Views: 1735

Welcome emails for new users!

Post #: 48
Date: 2012-10-29 21:41:28.000
Author: Rocket Man

When I started this little thing a month and a half ago, I didn't expect anyone to notice it right away. The Internet is a big place and Rockets is, for now, quite small.

But a few people have found their way here (welcome guys!) and so I now need a way to send some welcome emails for new users.

I've added a function called (send-mail), which sends an email to a given address. Behind the scenes it uses the Unix command sendmail, so you will need to have that installed on your server (simply type "sudo apt-get install sendmail" on Ubuntu or Debian).

To avoid emails falling into spam traps, you need to be sending from a valid email address that has the same domain as the server that is sending it. The (send-mail) function will take care of all the headers and things automatically.

For example, for new users, I simply use:

(send-mail UserEmail "" "Rocket Man" "Welcome to the newLISP on Rockets blog!" welcome-email)

where welcome-email is the string of the body of the email.

Views: 1699