Wednesday, May 16, 2018

Run LibreOffice in PHP scipts

It is often desirable to have PHP convert Word documents into PDF documents. LibreOffice can do that very well and of course it can do much more. There are three tricky points to consider if you want to run LibreOffice with PHP:

  1. You need to run it using the --headless option as it will probably be running on a server that probably has no GUI capabilities.
  2. You need to make sure that the output dir (which if not explicitly specified is the same as that of the php script that runs LibreOffice) is writable by the user php runs as (in my case apache). Obviously, the directory where the document that will be read in resides (as well as the document itself) need to be readable by the user php runs as (in my case I just did a chown apache:apahe <dirpath> on both these dirs)
  3. (Trickiest) soffice (one of LibreOffice's executables) will still not run correctly as apache (or whatever your php user is) although it will run fine if you execute it as root. This is because it requires special permissions for a directory that it considers its environment dir. there is a number of ways to fix this but easiest is to use the -env:UserInstallation option. In my case I created a subdir under /tmp (specifically: /tmp/soffice) and gave it apache:apache ownership.
In conclusion here is how I successfully call soffice from my php script (where $target_file is the Word file to be read in):

$cmd2 = "/opt/libreoffice6.0/program/soffice -env:UserInstallation=file:///tmp/soffice
        --headless --convert-to pdf " . $target_file;

exec($cmd2);

In the my case, the resulting pdf file is saved in the same directory as the php file that runs sofficewhich is fine for my purposes.

Tuesday, March 27, 2018

PHP can't write to directory although it should

If PHP is not writing to a directory (e.g. you can't upload files) although all directory permissions as well as PHP and Apache settings and configuration are correct then, in case you are on CentOS 7 (or RedHat 7), the culprit may be SELinux. Just disable it or, if needed, allow writing to the directory of interest using chcon command:

To disable SELinux

To resolve the issue with SELinux enabled

Thursday, March 8, 2018

How to configure drupal with clean URLs

There is some confusion on drupal.org (and other sites) on how exactly to do that: multiple conflicting articles, incomplete instructions, etc. The way to do it is to basically include the correct set of httpd.conf directives for all three of the following directories (assuming the default/usual directories are used):
  • /var/www 
  • /var/www/html
  • /var/www/html/sites/default
The httpd.conf sections for the first two directories normally already exist by default; the third one needs to be created. No .htaccess files need to be used; everything can be done in the httpd.conf file. Here is the relevant listing from my httpd.conf file that successfully configured Drupal with clean URLs for me:

#
# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
#
DocumentRoot "/var/www/html"

#
# Relax access to content within /var/www.
#
<Directory "/var/www">
    AllowOverride All
    # Allow open access:
    Require all granted
</Directory>

# Further relax access to the default document root:
<Directory "/var/www/html">
    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    #
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    #
    # The Options directive is both complicated and important.  Please see
    # http://httpd.apache.org/docs/2.4/mod/core.html#options
    # for more information.
    #
    Options Indexes FollowSymLinks
    #Options Includes ExecCGI FollowSymLinks MultiViews

    #
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   Options FileInfo AuthConfig Limit
    #
    #AllowOverride None
    AllowOverride All

    #
    # Controls who can get stuff from this server.
    #
    Require all granted

        RewriteEngine on
        RewriteBase /
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        #RewriteCond %{REQUEST_URI} !=/favicon.ico
        #RewriteRule ^ index.php [L]
        RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

</Directory>

<Directory "/var/www/html/sites/default">
    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    #
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    #
    # The Options directive is both complicated and important.  Please see
    # http://httpd.apache.org/docs/2.4/mod/core.html#options
    # for more information.
    #
    Options Indexes FollowSymLinks
    #Options Includes ExecCGI FollowSymLinks MultiViews

    #
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   Options FileInfo AuthConfig Limit
    #
    #AllowOverride None
    AllowOverride All

    #
    # Controls who can get stuff from this server.
    #
    Require all granted

        RewriteEngine on
        RewriteBase /
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        #RewriteCond %{REQUEST_URI} !=/favicon.ico
        #RewriteRule ^ index.php [L]
        RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

</Directory>


How to integrate PDFBox into WildFly 11

There are no specific instructions on how to do this. Here is how I did it and it works fine for my project:

  1. Add the three main PDFBox jar files as a WildFly module. To do this you have to create a new appropriately named folder under WildFly's installation directory. Like this: <some random directory>/wildfly-11.0.0.Final/modules/system/layers/base/org/apache/pdfbox/main

  2. Then, in that directory wget the three main PDFBox jar files. Currently the latest are the  ones that also appear in the XML listing right below.

  3. Then, in the same directory, create an XML file , named module.xml, thusly:

    <?xml version="1.0" encoding="UTF-8"?>
    <module xmlns="urn:jboss:module:1.5" name="org.apache.pdfbox">
        <resources>
            <resource-root path="pdfbox-2.0.8.jar"/>
            <resource-root path="fontbox-2.0.8.jar"/>
            <resource-root path="xmpbox-2.0.8.jar"/>
        </resources>
    
        <dependencies>
            <module name="javax.api"/>
            <module name="org.apache.commons.logging"/>
        </dependencies>
    </module>
    

  4. Now what we need is to declare the newly added pdfbox WildFly module as a global one, i.e. one that can be referenced from any deployment in our WildFly instance. To do this we need to edit our standalone.xml or domain.xml file. In the case of out pdfbox module the global declaration needs to be done under the <subsystem xmlns="urn:jboss:domain:ee:4.0"> section of the file which, for WildFly 11, it already exists and is at version 4.0. Please note that for other WildFly configurations you may need to create it altogether or you may have a different ee version number. In my case what I did is I added the following simple XML markup at the end of the section:

    <global-modules>
        <module name="org.apache.pdfbox" slot="main" />
    </global-modules>
    

  5. Restart your WildFly and you should be on!

Monday, March 5, 2018

Date expiration (licensing) in a VSTO Office Add-In

Sometimes you want to have a VSTO Office add-in you've developed stop working on a specific date; this is usually done for licensing related purposes.

Here's how to gracefully stop a VSTO Office add-in from working before it even loads any Ribbon elements. Basically you have to modify the code in your ThisAddIn class to look like this:

    public partial class ThisAddIn
    {
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
        }

        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
        }

        private Boolean isExpired()
        {
            DateTime end = DateTime.Parse("2018-03-21"); //This can be any date you like
            DateTime today = DateTime.Today;

            if (today >= end)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        protected override Microsoft.Office.Core.
            IRibbonExtensibility CreateRibbonExtensibilityObject()
        {
            if (!isExpired())
            {
                return new Ribbon1(); //Or whatever your ribbon class is called
            }
            else
                return null;
        }

        ... //Unrelated code from here on 
    }

Don't try to stop the Add-In from loading in the ThisAddIn_Startup() method as this gets called after the Add-In's Ribbon elements get loaded and display in the Office application.

Sunday, February 25, 2018

Find which process holds a particular connection to a MySQL database

Finding which process holds a particular connection to a MySQL database is often not immediately obvious or well documented.

First do this in your mysql command line client:
mysql> show full processlist;
+-----+--------+-----------------+----------+---------+------+----------+-----------------------+
| Id  | User   | Host            | db       | Command | Time | State    | Info                  |
+-----+--------+-----------------+----------+---------+------+----------+-----------------------+
|   2 | root   | localhost       | adspider | Query   |    0 | starting | show full processlist |
| 257 | fotios | localhost:35828 | adspider | Sleep   |   20 |          | NULL                  |
| 259 | fotios | localhost:35832 | adspider | Sleep   |   17 |          | NULL                  |
| 261 | fotios | localhost:35836 | adspider | Sleep   |    3 |          | NULL                  |
| 263 | fotios | localhost:35840 | adspider | Sleep   |    1 |          | NULL                  |
+-----+--------+-----------------+----------+---------+------+----------+-----------------------+
5 rows in set (0.00 sec)

Then, on your Linux command line do this to see which process holds the socket to the port of interest:

[root@li1849-192 ~]# netstat -np | grep 35828
tcp6       0      0 127.0.0.1:35828         127.0.0.1:3306          ESTABLISHED 28509/java
tcp6       0      0 127.0.0.1:3306          127.0.0.1:35828         ESTABLISHED 28432/mysqld

In my case the connection to MySQL was being made by a java process via JDBC