Donnerstag, 5. Dezember 2013

Delphi and the (still a little bit missing) ternary operator

One of the things I missed in Delphi is the ternary operator, that I learned to love and use in other programming languages. It helps saving lines of code and in most cases is more readable than the longer annotation. Look for example at the following example in C/C++/Java syntax:
if SomeBooleanVal {
  a = b;
} else {
  a = c;
}
SomeFunction(a);

There are several lines of code which could be just one easy readable line width the same meaning:
SomeFunction(SomeBooleanVal ? b : c);

Since Delphi 7 (or 6?) there is an IfThen function in StrUtils and Math lib supporting similar functionality for string, Integer, Int64 and Double data types.

So look at the following example for detailed code:
if SomeBooleanVal
  a := b
else
  a := c;
SomeFunction(a);

And now at the much shorter code:
SomeFunction(IfThen(SomeBooleanVal, b, c);

While it helps keeping the code simpler, it sill has its limitations. The function is only defined for four data types and you would have to write another function for every data type you need which is not one of the four. The other limitation is simply the fact that it is a function and not a compiler feature like in other languages so there is no laziness possible. If you submit something more complex as b and c parameters like d *10 - e / 5, both values are evaluated first and the result of them sent to the function which would return the correct result depending on the boolean value. In C only the needed of both values would be evaluated and assigned to the target variable.
Of course with today's machines it's not the most ugly loss of efficiency, it still shows some limitations of a language design.

Edit:
+Eric Grange pointed another simple case which IfThen can't handle:
a := IfThen(c<>0, b/c)

Because evaluation happens before check you will get division by zero if c is zero.

Mittwoch, 4. Dezember 2013

Delphi and missing Windows API functions

When I had to write a Windows service using Delphi 2010, which is possible thanks to WinSvc.pas, which is based on winsvc.h from Windows SDK, I found several functions just missing from the pascal unit. One of the functions I missed was ChangeServiceConfig2, which I could use to set extended service properties without fiddling with registry and setting some string and DWORD values.
I decided to go deep into the old SDKs to see what version the unit represents and was very surprised that it still had more or less the stand of Win9x line, though I'm not sure of WinME because I couldn't find it's SDK or advapi32.dll. While at least the function EnumServicesStatusEx was already in NT4.0 (but not in Win98FE), many new functions came with Win2k SDK (including ChangeServiceConfig2 which I intended to use) which was released in 2000. With Delphi support for WinXP (released in 2001) which is supported since Delphi 7 (released in 2002) they still didn't implement the new and missing WinAPI bindings and this seems to be still the situation as of today.

I decided to properly implement all the missing functions and structures of winsvc.h from Win2k SDK and so far have added extensions from WinXP SDK. (Here it's just the same as Win2k + two new constants) and currently I'm in the process of adding the extensions which came with Windows Vista, but this is WIP yet.

Because the might be several other API binding missing I decided to create a small project for this, so all the binding extensions can live in one central place. You will find the WIP source and the "stable" releases of the units here: https://github.com/rarog/winapi-for-delphi.

Dienstag, 26. November 2013

jQuery sticky sidebar plugin

For a small project I had to search for a new sticky sidebar plugin which would meet my needs. Previously I used the plugin made by p-m-p but it had some issues with not dealing with a position to stop and if the element was not fitting to screen it moved it below the site bottom edge, so the new edge was prolonged. A user could almost scroll into infinity.

Some days ago I found another sticky sidebar plugin made by Spoiled Milk which seems to have almost perfect functionality for my purposes, it can be configured pretty good. The only small issue I had I could circumvent by adding one line and modifying another line of code to be perfect for me. My problem was that the top edge of the element I wanted to be sticky wasn't just below a generic horizontal element but one of several in a vertical row with my element being the last, so it wasn't sufficient to just get the element above my sticky element as header and getting it's height like the plugin does it as of the time of this post being written, I have to add the top offset relative to the top edge of the document.

Search for the follwing line:
var breakingPoint1 = headerHeight + navHeight;

Replace it with the following two lines:
var headerTop = $(config.headerSelector).offset().top;
var breakingPoint1 = headerTop + headerHeight + navHeight;

Et voilà, it works as intended!

Freitag, 22. November 2013

Magento, Plesk, nginx and PHP-FPM

Since v11.5 Plesk has the ability to manage PHP websites without Apache by using the ability of nginx to run PHP directly via PHP-FPM, this is considered to be a faster way to run PHP scripts with generally lower memory footprint as nginx is really small.

Magento needs rewrite feature for internal SEO, while Apache has his mod_rewrite and the ability to use .htaccess files to configure its features, nginx doesn't offer such dynamic configuration, it's done statically so it doesn't have to search the path for .htaccess files and try to parse it, which happens on every webserver access and the price of slowing down the webservice while keeping the flexibility.

There are several tutorials across the web (e.g. nginx wiki) which show how to configure nginx, so Magento will run without .htaccess which are quite good but will not work with Plesk, which of course adds some own nginx configuration that collides with the nginx configuration in the tutorials.

The general problem is that Plesk defines its own "location /" and while you can work it around by using "location ~ (.*)" for the most important instruction "try_files $uri $uri/ @handler;" to run "location @handler { rewrite / /index.php; }", this will not work for urls with trailing slash which Magento generates for example for CMS sites, these will deliver 404 because Plesk adds following to every nginx config:
    location ~ /$ {
        index index.html index.cgi index.pl index.php index.xhtml index.htm index.shtml;
    }

The Plesk per-site nginx custom config is included inside the server scope, so a workaround using a combination of some ifs and rewrites is possible to circumvent this.

The following nginx config pasted into the Plesk custom nginx seems to do the magic properly:

## Taken from http://www.linux-magazin.de/Ausgaben/2012/01/Magento-Hosting/%28offset%29/4
# gzip compression
gzip on;
gzip_min_length 1100;
gzip_buffers 4 8k;
gzip_proxied any;
gzip_types text/plain text/css application/x-javascript text/javascript application/json;

############################################
## uncomment next line to enable light API calls processing
#rewrite ^/api/([a-z][0-9a-z_]+)/?$ /api.php?type=$1 break;

############################################
## rewrite API2 calls to api.php (by now it is REST only)
rewrite ^/api/rest /api.php?type=rest last;

############################################
## TRACE and TRACK HTTP methods disabled to prevent XSS attacks
if ($request_method ~ "^TRAC[EK]") {
 return 405;
}

############################################
## always send 404 on missing files in these folders
if ($uri !~ "^/(media|skin|js)/") {
 set $rewrite_to_index 1;
}

###########################################
## Deny access to release notes to prevent disclosure of the installed Magento version
if ($uri ~* "/RELEASE_NOTES.txt") {
 return 404;
}

# Don't rewrite if file exists
if (-e $request_filename) {
 set $rewrite_to_index 0;
}

############################################
## rewrite everything else to index.php
if ($rewrite_to_index = "1") {
 rewrite / /index.php;
}

############################################
## Prevent character encoding issues from server overrides
## If you still have problems, use the second line instead
charset off;
#charset utf-8;

############################################
## Add default Expires header
## http://developer.yahoo.com/performance/rules.html#expires
expires 365d;

##Taken from http://wiki.nginx.org/Magento
# Hide the system directories
location ~ ^/(app|includes|lib|media/downloadable|pkginfo|report/config.xml|var)/ {
 internal;
}

# Hide the hidden files
location /. {
 return 404;
}

Donnerstag, 21. November 2013

Delphi, ScrollBox and mouse wheel

I had a problem using Delphi 2010 that wasn't too easy to solve. Having a ScrollBox on a Frame or a Form and reacting to OnMouseWheel event isn't trivial. The problem seems to be in the VCL implementation of the TScrollBox control.

In my case I have several panels (TPanel) inside the scrollbox, testing it's OnMouseWheel event revealed that it was never fired. Probably this is because the mouse is above one of the panels, it gets the OnMouseWheel event, says that it handled it because it hasn't anything to scroll, and I have the current situation.

After trying different solutions I found the WinAPI style by intercepting and resending the WM_MOUSEWHEEL event to the scrollbox too complicated and finally found a working though a little bit hacky solution.

To solve it you need to use the OnMouseWheel event of the frame or form, where the scrollbox is.


procedure TMyFrame.FrameMouseWheel(Sender: TObject;
  Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
  var Handled: Boolean);
var
  LTopLeft, LTopRight, LBottomLeft, LBottomRight: Integer;
  LPoint: TPoint;
begin
  inherited;

  // First you have to get the position of the control on screen
  // as MousePos coordinates are based on the screen positions.
  LPoint := ScrollBox1.ClientToScreen(Point(0,0));
  LTopLeft := LPoint.X;
  LTopRight := LTopLeft + ScrollBox1.Width;
  LBottomLeft := LPoint.Y;
  LBottomRight := LBottomLeft + ScrollBox1.Width;

  if (MousePos.X >= LTopLeft) and
    (MousePos.X <= LTopRight) and
    (MousePos.Y >= LBottomLeft)and
    (MousePos.Y <= LBottomRight) then
  begin
    // If the mouse is inside the scrollbox coordinates,
    // scroll it by setting .VertScrollBar.Position.
    ScrollBox1.VertScrollBar.Position :=
      ScrollBox1.VertScrollBar.Position - WheelDelta;
    Handled := True;
  end;
end;

Funny part of it, you have to set .VertScrollBar.Position and mustn't use the ScrollBy function of the scrollbox as the latter doesn#t look for valid values and you can scroll out of screen beyond the actual content of the scrollbox.