wp-dokku

about dokku

Docker powered mini-Heroku. The smallest PaaS implementation you’ve ever seen.

Dokku allow us to run an app inside docker containers and scale easily -just like heroku does-

We have a microsite made with wordpress which we wanted to move inside dokku. The first thing you need is to install duplicator in your original website, create a package and build it, download the installer.php and the zip file.

We’ll use the dokku-wordpress script to generate the required files. It has the following requirements

A Dokku server. Tested on 0.7.2+
The dokku-mysql plugin
make installed locally
curl or wget installed locally

To install the dokku-mysql plugin just ssh into the dokku server and run

sudo dokku plugin:install https://github.com/dokku/dokku-mysql.git mysql

Then clone the dokku-wordpress repository to your local machine, run the command and follow the instructions that are printed in the command line from the last command and take note of the DATABASE_URL when you link the database container

git clone https://github.com/dokku-community/dokku-wordpress.git
cd dokku-wordpress
make build APP_NAME=testapp SERVER_NAME=<ip or name>

# run the following commands on the server to setup the app:

dokku apps:create testapp

# setup plugins persistent storage

mkdir -p /var/lib/dokku/data/storage/testapp-plugins
chown 32767:32767 /var/lib/dokku/data/storage/testapp-plugins
dokku storage:mount testapp /var/lib/dokku/data/storage/testapp-plugins:/app/wp-content/plugins

# setup upload persistent storage

mkdir -p /var/lib/dokku/data/storage/testapp-uploads
chown 32767:32767 /var/lib/dokku/data/storage/testapp-uploads
dokku storage:mount testapp /var/lib/dokku/data/storage/testapp-uploads:/app/wp-content/uploads

# setup your mysql database and link it to your app

export MYSQL_IMAGE_VERSION="5.6"
dokku mysql:create testapp-database
dokku mysql:link testapp-database testapp

# you will also need to set the proper environment variables for keys and salts
# the following were generated using the wordpress salt api: https://api.wordpress.org/secret-key/1.1/salt/
# and use the following commands to set them up:

dokku config:set testapp AUTH_KEY='Q<z4;VJkx#Jd;tV^d?ex^jwM^z/|b.$w,f4x[/vgCSVXKhZ|aO1p,'
dokku config:set testapp SECURE_AUTH_KEY='>zR1uL02;5+pDRA%HEz!~ty])hV^@;-s-`-rk`W+)zfdeWQ&e=yZM.j)M0Q4}xk1'
dokku config:set testapp LOGGED_IN_KEY=':5W#<&PO*_B7<Cj?7aLrbq|8PoF?2ZR.?_DAki*e3Oh!+RM@Ga@q6:L~(p=!D,SyHwa$B5.Fk'
dokku config:set testapp LOGGED_IN_SALT='xv$Lq]/6bowHaMF@Eff5fL/ab,-pu,P,Jh4U{uiIE~>mog<.O9gDW2+!IOo$M*='
dokku config:set testapp NONCE_SALT='dj8^5?t=V-,nV7}-{*bnz7b7vb.=N1:~BrMC`-D{GLl(k[+}!hh#D[tk#TcG;7|'

# now, on your local machine, change directory to your new wordpress app, and push it up

cd testapp
git push dokku master

then you'll have a vanilla wordpress deployed in dokku.

You should have a directory generated in the cloned folder named after your app, get into it and delete all the files except the following (notice there are hidden files)

.buildpacks
composer.json
composer.lock
.git

copy the files downloaded from duplicator inside this folder and deploy doing a git push dokku

open your app's url pointing to installer.php file and follow the instructions. Use the values from the DATABASE_URL generated earlier, if you didn't take note, you can get it by running the command

ssh -t dokku@<ip or name> config <appname>

use that values for the DB configuration step and continue with the setup.
for example if you have the following DATABASE_URL

mysql://mysql:7ab9b1b45db2232c@dokku-mysql-testapp-database:3306/testapp-database
the corresponding values would be

user=mysql
pass=7ab9b1b45db2232c
server=dokku-mysql-testapp-database
database=testapp-database

done! you should have cloned successfully your wordpress site into dokku.

Anuncios

jhipster-elastic

We have an Entity with an Enum field. This enum field gets saved in the database as string.

enum

public enum TipoContrato {
    ADJUDICACION_DIRECTA,  INVITACION_RESTRINGIDA,  LICITACION_PUBLICA
}

entity

@Entity
@Table(name = "contrato")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Document(indexName = "contrato")
public class Contrato implements Serializable {

    ...

    @Enumerated(EnumType.STRING)
    @Column(name = "tipo")
    private TipoContrato tipo;

the thing is, it includes underscore. Elasticsearch by default uses the standard analyzer which stores it and searches for it as one word. So, i.e. searching for ‘directa’ would not produce any results, but searching for ‘adjudicacion_directa’ would.

First you should enable http access to elasticsearch on your jhipster project to test if you’re getting results with a simple request, i.e using curl

curl -XPOST 'localhost:9200/contrato/_search?pretty' -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool" : {
      "should" : [
        { "match" : { "tipo" : "directa" } }
      ]
    }             
  }                                         
}
'

to get the results we’re expecting we need to change the analyzer that’s being used to index the field. We do this by changing the mapping. You could change it by using the REST api, and then reindexing your data. In development, we can reconfigure our mapping and then just do a mvn clean to regenerate it all.

Let’s change the mapping in our entity (we just need to add an annotation).

    @Enumerated(EnumType.STRING)
    @Column(name = "tipo")
    @Field(type = FieldType.String, analyzer = "simple")
    private TipoContrato tipo;

now, the results that came back from the curl request were fine. But the results from the search box didn’t. We had to include a custom search for it to work, cause the more general search doesn’t do a full text search apparently i.e. if you open in a new tab the url http://localhost:9200/contrato/_search?q=directa it doesn’t work but if you include the field it works http://localhost:9200/contrato/_search?q=tipo:directa

public interface ContratoSearchRepository extends ElasticsearchRepository<Contrato, Long> {

    @Query("{\n" +
        "  \"query\": {\n" +
        "    \"bool\" : {\n" +
        "      \"should\" : [\n" +
        "        { \"match\" : { \"_all\" : \"?0\" } },\n" +
        "        { \"match\" : { \"tipo\" : \"?0\" } }\n" +
        "      ]\n" +
        "    }             \n" +
        "  }                                         \n" +
        "}")
    Page<Contrato> buscarContrato(String query, Pageable page);

}

sources:
http://www.baeldung.com/spring-data-elasticsearch-tutorial
https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html
https://stackoverflow.com/questions/31837546/elasticsearch-splitting-words-on-underscore-search-founds-nothing
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html
https://docs.spring.io/spring-data/elasticsearch/docs/3.0.2.RELEASE/api/

Screenshot from 2018-01-11 16-16-39

When you generate an entity with jhipster entity and add a LocalDate the UI generated uses ng-bootstrap Datepicker. To change the format you need to add an implementation of the class NgbDateParserFormatter. To change the language you need to provide an implementation of the class NgbDatepickerI18n (as shown in the examples).

First create the class files, i.e.

/webapp/src/main/webapp/app/blocks/config/ng-bootstrap.date-parser-formatter.ts

import { Injectable } from '@angular/core';
import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';

function padNumber(value: number) {
    if (isNumber(value)) {
        return `0${value}`.slice(-2);
    } else {
        return "";
    }
}

function isNumber(value: any): boolean {
    return !isNaN(toInteger(value));
}

function toInteger(value: any): number {
    return parseInt(`${value}`, 10);
}


@Injectable()
export class NgbDateParserFormatterEsMX extends NgbDateParserFormatter {
    parse(value: string): NgbDateStruct {
        if (value) {
            const dateParts = value.trim().split('/');
            if (dateParts.length === 1 && isNumber(dateParts[0])) {
                return {year: toInteger(dateParts[0]), month: null, day: null};
            } else if (dateParts.length === 2 && isNumber(dateParts[0]) && isNumber(dateParts[1])) {
                return {year: toInteger(dateParts[1]), month: toInteger(dateParts[0]), day: null};
            } else if (dateParts.length === 3 && isNumber(dateParts[0]) && isNumber(dateParts[1]) && isNumber(dateParts[2])) {
                return {year: toInteger(dateParts[2]), month: toInteger(dateParts[1]), day: toInteger(dateParts[0])};
            }
        }
        return null;
    }

    format(date: NgbDateStruct): string {
        let stringDate: string = "";
        if(date) {
            stringDate += isNumber(date.day) ? padNumber(date.day) + "/" : "";
            stringDate += isNumber(date.month) ? padNumber(date.month) + "/" : "";
            stringDate += date.year;
        }
        return stringDate;
    }
}

<project>/direccion-juridica/webapp/src/main/webapp/app/blocks/config/ng-bootstrap-datepicker-i18n.ts

import {Component, Injectable} from '@angular/core';
import {NgbDatepickerI18n} from '@ng-bootstrap/ng-bootstrap';

const I18N_VALUES = {
    'es-MX': {
        weekdays: ['Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab', 'Dom'],
        months: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
    }
    // other languages you would support
};

// Define a service holding the language. You probably already have one if your app is i18ned. Or you could also
// use the Angular LOCALE_ID value
@Injectable()
export class I18n {
    language = 'es-MX';
}

// Define custom service providing the months and weekdays translations
@Injectable()
export class CustomDatepickerI18n extends NgbDatepickerI18n {

    constructor(private _i18n:I18n) {
        super();
    }

    getWeekdayShortName(weekday:number):string {
        return I18N_VALUES[this._i18n.language].weekdays[weekday - 1];
    }

    getMonthShortName(month:number):string {
        return I18N_VALUES[this._i18n.language].months[month - 1];
    }

    getMonthFullName(month:number):string {
        return this.getMonthShortName(month);
    }
}

finally define the provider in <project>/direccion-juridica/webapp/src/main/webapp/app/app.module.ts

import ...

...
    providers: [
        ProfileService,
        customHttpProvider(),
        PaginationConfig,
        UserRouteAccessService,
        I18n,
        {provide: NgbDatepickerI18n, useClass: CustomDatepickerI18n},
        {provide: NgbDateParserFormatter, useClass: NgbDateParserFormatterEsMX}
    ],
    bootstrap: [ JhiMainComponent ]
})
...

to change the displayed format of dates in tables you can replace in files date:'mediumDate' with date:'dd/MM/yyyy' and date:'medium' with date:'dd/MM/yyyy HH:mm' for example.

for ZonedDateTime it uses a datetime-local input which tries to use browser configuration -if supported-
Due to, and I’m quoting this from Mozilla

Date/time inputs sound convenient at first glance; they provide an easy UI 
for choosing dates and times, and they normalize the data format sent to the 
server, regardless of the user's locale. However, there are issues with 
 because of the **limited browser support.**

So I would recommend breaking DateTimes into a Datepicker and a Timepicker from ng-bootstrap and configuring as above.

sources:
https://stackoverflow.com/questions/40664523/angular2-ngbdatepicker-how-to-format-date-in-inputfield

https://github.com/ng-bootstrap/ng-bootstrap/issues/754#issuecomment-247767027

25478_D5100_left

Tengo una cámara Nikon D5100 y nunca he podido establecer correctamente la fecha de la cámara, lo intenté algunas veces y siempre me daba una diferencia al ver las propiedades de la imagen en finder o en photos. No sé si por remover la batería, la zona horaria, horario de verano, al formatear la memoria o a que se deba.
Por ello, y debido a la importancia para el correcto ordenamiento en las aplicaciones de fotografía, necesitaba corregir las propiedades de las fotografías antes de importar los archivos a cualquier programa. Para ello existe una herramienta de línea de comandos (CLI) llamada ‘exiftool’. Para instalarla mediante brew simplemente ejecuta

brew install exiftool

después de ello posicionate en la carpeta donde tengas las fotografías y ejecuta el siguiente comando para corregir la hora de las fotografías (agregar 5 horas a la hora original, preservar las fechas de sistema y modificar directamente sin crear una copia)

cd /tmp/cancun
exiftool -alldates+=5 -P -overwrite_original_in_place *

nota: primero copie las fotografías de la memoria de la cámara a una carpeta temporal, por ello utilicé la opción -overwrite_original_in_place, si no estas seguro no la uses y por default creará un respaldo de las imágenes originales.

fuentes:

https://petapixel.com/2012/11/05/how-to-fix-your-timestamps-if-you-forgot-to-update-your-camera-for-daylight-savings/
https://sno.phy.queensu.ca/~phil/exiftool/faq.html#Q24

I had a form with ngSubmit

<form name="editForm" role="form" novalidate (ngSubmit)="save()" #editForm="ngForm">

and inside this form I have 3 buttons like so

<div class="btn-group flex-btn-group-container">

            <span class="fa fa-eye"></span>
            <span class="hidden-md-down">Descargar</span>


            <span class="fa fa-pencil"></span>
            <span class="hidden-md-down">Edit</span>


            <span class="fa fa-remove"></span>
            <span class="hidden-md-down">Delete</span>

</div>

two are of type submit and one has a click handler.

Whenever I clicked the first button (click handler), it was submitting the form. To prevent this you should add the $event parameter

<button (click) = "descargarAdjunto(adjunto, $event)" class="btn btn-info btn-sm">
    <span class="fa fa-eye"></span>
    <span class="hidden-md-down">Descargar</span>
</button>

and call the prevent default

private descargarAdjunto(adjunto, $event){
        $event.preventDefault()
        ...
}

fuentes:
https://coderwall.com/p/hvy1rg/angularjs-how-to-prevent-form-submission-after-click-on-button
https://github.com/angular/angular.js/issues/6017
https://docs.angularjs.org/api/ng/directive/form

angular – debugging an app

noviembre 24, 2017

Peek 2017-11-24 15-39.gif

There’s an excellent article on ionic blog which talks about how to debug an app. It become really handy now that JS Batarang extension stopped working on my google chrome 😥 (Ubuntu 16.04.3 LTS; Chrome Version 62.0.3202.62 (Official Build) (64-bit)).

Peek 2017-11-24 15-23

You just have to identify an element on your page which is associated with an scope and you would see all the variables on that scope through the use of a simple console.log call.

angular.element(targetNode).scope()

sources:
http://blog.ionicframework.com/angularjs-console/

I have an enum in one of our entities like so

private enum ConduccionAlProceso {
        POR_COMPARECENCIA, POR_CITACION, CON_CONTROL_DE_DETENCION, EN_CUMPLIMIENTO_DE_ORDEN_DE_APREHENSION
    }

I wanted to display the values on client side without underscores. There are a lot of articles showing how to do so by using a filter directive like

App.filter('underscoreless', function () {
  return function (input) {
      return input.replace(/_/g, ' ');
  };
});

and then in html

{{ addText | underscoreless }}

but I didn’t want to create a directive for this since i’m only using it once.

So, I ended up adding a function in the controller

$scope.mediosConduccionAlProceso = [
    'POR_COMPARECENCIA', 'POR_CITACION', 'CON_CONTROL_DE_DETENCION', 'EN_CUMPLIMIENTO_DE_ORDEN_DE_APREHENSION'
]
$scope.replaceUnderscores = function(item){
    return item.replace(new RegExp('_','g'), ' ');
}

and calling it in the html like

    <select class="form-control"
            ng-model="carpeta.conduccionAlProceso"
            ng-options="medio as replaceUnderscores(medio) for medio in mediosConduccionAlProceso 
                        | orderBy: 'toString()'"
            required>
        <option value=""></option>
    </select>

sources:
https://stackoverflow.com/questions/31939288/angular-filter-to-replace-all-underscores-to-spaces
https://stackoverflow.com/questions/1144783/how-to-replace-all-occurrences-of-a-string-in-javascript
https://stackoverflow.com/questions/21785021/can-ngoptions-call-a-function-to-get-display-text
https://docs.angularjs.org/api/ng/directive/ngOptions