WordPress: un plugin per un widget che mostra il link ad un post o una pagina con riassunto e immagine in evidenza.

widget-mspagelinkQuesto è un plugin davvero semplice, serve a mostrare nelle aree dei widget, un link con il titolo di una pagina (o di un post), l’immagine in evidenza (se caricata) e opzionalmente anche il riassunto del contenuto della pagina.

Lo schema per scrivere il widget l’ho preso da questo sito: http://www.wpbeginner.com/wp-tutorials/how-to-create-a-custom-wordpress-widget/ che spiega molto facilmente come costruirsi il proprio widget personalizzato. Il widget è stato poi inserito in un plugin.

Una caratteristica di questo widget è quella di poter mostrare un riassunto anche nel caso delle “pagine” di WordPress nelle quali a differenza dei post, non è possibile inserire l’ excerpt. La funzione che ho utilizzato è pubblicata qui: http://fullrefresh.com/2013/08/02/getting-a-wp-post-excerpt-outside-the-loop-updated/ . E’ molto utile perché non solo permette di stabilire la lunghezza del riassunto in parole, senza quindi troncare il testo, ma esegue anche un accurata rimozione degli shortcode mantenendo i ritorni a capo e la formattazione di base.

widget-mspagelink-adminpanelIn questa immagine si vede come viene mostrato il widget nel pannello amministrativo e i settaggi che è possibile impostare.

L’unico parametro che è veramente necessario inserire è l’ID del post o della pagina. Viene anche effettuato un controllo che questo corrisponda ad un articolo effettivamente esistente.

I parametri sono:

  1. Titolo (Titolo del Widget)
  2. Post ID (ID del post / pagina)
  3. Dimensione dell’immagine (a scelta: “thumbnail”,”medium”,”large”,”full”)
  4. Allineamento dell’immagine (a scelta: “aligncenter”,”alignleft”,”alignright”)
  5. Mostra il riassunto (si / no)
  6. Lunghezza riassunto (lunghezza in numero di parole)

Ed ecco il codice:

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<?php
/*
Plugin Name: MS Page Link
Plugin URI: http://www.spadamar.com/?p=713
Description: A plugin to add a simple widget to show a link to a page or post with the title, the featured image and optionally the excerpt.
Author: Mario Spada <spadamar@spadamar.com>
Version: 1.0
Author URI: http://www.spadamar.com
License: GPLv2
Text Domain:ms-pagelink
Domain Path:/lang/
*/
 
class MS_Page_link extends WP_Widget {
 
	public function __construct() {
		$widget_ops = array('classname' => 'MS_Page_link', 'description' => __('Displays a link to a page or a post with featured image!','ms-pagelink') );
		$this->WP_Widget('MS_Page_link', __('MS Page Link','ms-pagelink'), $widget_ops);
	}
 
	function widget($args, $instance) {
		extract($args, EXTR_SKIP);
		$title = empty($instance['title']) ? ' ' : apply_filters('widget_title', $instance['title']);
		$page_ID = empty($instance['page_ID']) ? '' : $instance['page_ID'];
		$img_size = empty($instance['img_size']) ? 'thumbnail' : $instance['img_size'];
		$img_alignment = empty($instance['img_alignment']) ? 'aligncenter' : $instance['img_alignment'];
		$show_excerpt = $instance['show_excerpt'] ? true : false;
		$excerpt_lenght = empty($instance['excerpt_lenght']) ? 35 : $instance['excerpt_lenght'];
		echo (isset($before_widget)?$before_widget:'');
		if (!empty($title))
		  echo $before_title . $title . $after_title;
		if (!empty($page_ID) && $this->does_post_exists($page_ID)) {
			echo "<div class='MSPagelink'>";
		  $featured_img = get_the_post_thumbnail( $page_ID, $img_size, array('class' => $img_alignment)); // (thumbnail, medium, large o full) ( alignleft , alignright , or aligncenter )
		  if (!empty($featured_img)) {
			echo "<div class='MSPagelink-img'>".$featured_img."</div>";
		  }
		  $style = "text-align:center";
		  echo '<p class="MSPagelink-txt" style="'.$style.'"><a href="'.get_permalink( $page_ID ).'">'.get_the_title( $page_ID ).'</a></p>';
		  if ($show_excerpt) {
			$excerpt_lenght = (is_numeric($excerpt_lenght) && $excerpt_lenght > 0) ? $excerpt_lenght : 35;
			$this_excerpt = $this->fr_excerpt_by_id($page_ID,$excerpt_lenght);
			echo "<div class='MSPagelink-excerpt'>".$this_excerpt."</div>";
		  }
		  echo "</div>";
		} else {
			echo sprintf( __( '<p>Page %s doesn\'t exist.</p>', 'ms-pagelink' ), $page_ID );
		}
		echo (isset($after_widget)?$after_widget:'');
	}
 
  public function form( $instance ) {
     $instance = wp_parse_args( (array) $instance, array( 'title' => '' ) );
     $title = $instance['title'];
     $page_ID = $instance['page_ID'];
     $img_size = $instance['img_size'];   
	 $img_alignment = $instance['img_alignment'];
	 $excerpt_lenght = $instance['excerpt_lenght'];
     ?>
     <p>
      <label for="<?php echo $this->get_field_id('title'); ?>"><?php echo __('Title:','ms-pagelink');?> 
        <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" 
               name="<?php echo $this->get_field_name('title'); ?>" type="text" 
               value="<?php echo esc_attr($title); ?>" />
      </label>
     </p>
     <p>
      <label for="<?php echo $this->get_field_id('page_ID'); ?>"><?php echo __('Post ID:','ms-pagelink');?> 
        <input class="widefat" id="<?php echo $this->get_field_id('page_ID'); ?>" 
               name="<?php echo $this->get_field_name('page_ID'); ?>" type="text" 
               value="<?php echo esc_attr($page_ID); ?>" />
      </label>
     </p>
     <p>
	 <?php
		$options = array("thumbnail","medium","large","full");
	 ?>
      <label for="<?php echo $this->get_field_id('img_size'); ?>"><?php echo __('Image size:','ms-pagelink');?> 
        <select class="widefat" id="<?php echo $this->get_field_id('img_size'); ?>" 
               name="<?php echo $this->get_field_name('img_size'); ?>">
		<?php
		foreach($options as $opt) {
			$selected = $opt == esc_attr($img_size) ? "selected='selected'" : "";
			echo "<option $selected>$opt</option>\n";			
		}
		?>
 
        </select>
      </label>
     </p>
     <p>
	 <?php
		$options = array("aligncenter","alignleft","alignright");
	 ?>
      <label for="<?php echo $this->get_field_id('img_alignment'); ?>"><?php echo __('Image alignment:','ms-pagelink');?> 
        <select class="widefat" id="<?php echo $this->get_field_id('img_alignment'); ?>" 
               name="<?php echo $this->get_field_name('img_alignment'); ?>">
		<?php
		foreach($options as $opt) {
			$selected = $opt == esc_attr($img_alignment) ? "selected='selected'" : "";
			echo "<option $selected>$opt</option>\n";			
		}
		?>          
        </select>
      </label>
      </p>
 
	  <p>
		<input class="checkbox" type="checkbox" <?php checked($instance['show_excerpt'], 'on'); ?> id="<?php echo $this->get_field_id('show_excerpt'); ?>" name="<?php echo $this->get_field_name('show_excerpt'); ?>" /> 
		<label for="<?php echo $this->get_field_id('show_excerpt'); ?>"><?php echo __('Show excerpt','ms-pagelink');?></label>
	  </p>
 
     <p>
      <label for="<?php echo $this->get_field_id('excerpt_lenght'); ?>"><?php echo __('Excerpt lenght:','ms-pagelink');?> 
        <input class="widefat" id="<?php echo $this->get_field_id('excerpt_lenght'); ?>" 
               name="<?php echo $this->get_field_name('excerpt_lenght'); ?>" type="text" 
               value="<?php echo esc_attr($excerpt_lenght); ?>" />
      </label>
     </p>
 
     <?php
 
  }
 
  function update($new_instance, $old_instance) {
    $instance = $old_instance;
    $instance['title'] = $new_instance['title'];
    $instance['page_ID'] = $new_instance['page_ID'];
    $instance['img_size'] = $new_instance['img_size'];
    $instance['img_alignment'] = $new_instance['img_alignment'];
    $instance['show_excerpt'] = $new_instance['show_excerpt'];
    $instance['excerpt_lenght'] = (int) $new_instance['excerpt_lenght'];
    return $instance;
  }
 
	public function does_post_exists( $id ) {
		return is_string( get_post_status( $id ) );
	}
 
	/*******************************************************************
	 * 
	 * The function below come from here: 
	 * http://fullrefresh.com/2013/08/02/getting-a-wp-post-excerpt-outside-the-loop-updated/
	 * 
	 * ****************************************************************/
 
	public function fr_excerpt_by_id($post_id, $excerpt_length = 35, $line_breaks = TRUE){
		$the_post = get_post($post_id); //Gets post ID
		$the_excerpt = $the_post->post_excerpt ? $the_post->post_excerpt : $the_post->post_content; //Gets post_excerpt or post_content to be used as a basis for the excerpt
		$the_excerpt = apply_filters('the_excerpt', $the_excerpt);
		$the_excerpt = $line_breaks ? strip_tags(strip_shortcodes($the_excerpt), '<p><br>') : strip_tags(strip_shortcodes($the_excerpt)); //Strips tags and imagesidW2rTuV
		$words = explode(' ', $the_excerpt, $excerpt_length + 1);
		if(count($words) > $excerpt_length) :
		  array_pop($words);
		  array_push($words, '…');
		  $the_excerpt = implode(' ', $words);
		  $the_excerpt = $line_breaks ? $the_excerpt . '</p>' : $the_excerpt;
		endif;
		$the_excerpt = trim($the_excerpt);
		return $the_excerpt;
	}
 
}
 
function ms_page_link_widget_plugin() {
    register_widget( "MS_Page_link" );
}
add_action( 'widgets_init', 'ms_page_link_widget_plugin' );
 
/**
 * Register text domain for localization.
 */
function ms_page_link_textdomain() {
	load_plugin_textdomain( 'ms-pagelink', false, dirname( plugin_basename( __FILE__ ) ) . '/lang' );
}
add_action( 'plugins_loaded', 'ms_page_link_textdomain' );
 
?>

Infine il plugin è predisposto per le traduzioni, ed è già tradotto in inglese (default) e in italiano. E’ possibile aggiungere altre traduzioni semplicemente aggiungendo il file della lingua nella cartella /lang all’interno della directory del plugin.

 

PHP: come separare file di immagini jpg ordinandoli per orizzontali e verticali

V-HQuesto script PHP che deve essere utilizzato da linea di comando, permette di analizzare una directory per riconoscere e “marcare” le immagini con orientamento verticale da quelle con orientamento orizzontale. In questo modo è possibile separare i due blocchi di file, per esempio per comporre una galleria fotografica omogenea nel layout.

Lo script utilizza la funzione PHP getimagesize() per calcolare il rapporto fra altezza e larghezza di una immagine jpg. Se il rapporto è maggiore o uguale a 1 è verticale, altrimenti è orizzontale. (N.B.: le immagini quadrate risulteranno verticali…).

I parametri in ingresso sono 2. Il primo definisce il percorso della directory che contiene le immagini e il secondo indica se si vogliono rinominare i file o copiarli. Al nome dei file viene aggiunta una stringa “_H_” per indicare che l’immagine è orizzontale e “_V_” per le immagini ad orientamento verticale. Queste stringhe possono essere cambiate modificando i valori delle due costanti “PRE_H” e “PRE_V” alle righe 26 e 27 dello script.

Se lo script viene lanciato senza nessun parametro vengono analizzati i file della directory corrente e viene prodotto l’output dell’orientamento per ogni file e di come verrebero modificati i nomi dei file, senza però effettuare alcuna modifica.

Questo è il codice:

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
67
68
69
70
71
72
73
74
75
76
77
78
#!/usr/bin/php
<?php
/*
 * sortimages.php
 * 
 * Copyright 2015 mario spada <spadamar@spadamar.com>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 * 
 * 
 */
 
define("PRE_H","_H_");
define("PRE_V","_V_");
 
function getImgMode($filename) {
	$size = getimagesize($filename); 
	$width = $size[0]; 
	$height = $size[1]; 
	$aspect = $height / $width; 
	if ($aspect >= 1) $mode = "vertical"; 
	else $mode = "horizontal";
return $mode;
}  
 
$directory = empty($argv[1]) ? "./" : rtrim($argv[1], '/') . '/';
if (!file_exists($directory))
	die($directory." directory does not exist!");
 
$renameFls = empty($argv[2]) ? 0 : $argv[2];
 
if (!empty($renameFls)){
	if($renameFls == 'rename') {
		$renameFls = 1;
	} elseif($renameFls == 'copy') {
		$renameFls = 2;
	} else {
		$renameFls = 0;
	}
}
 
$images = glob($directory."*.{jpg,JPG,jpeg}", GLOB_BRACE);
 
foreach($images as $image) {
	$path_parts = pathinfo($image);
	$newFln = "";
	$action = " It will change in: ";
	$imgLayout = getImgMode($image);
	if($imgLayout=='horizontal') {
	  $newFln = $directory.PRE_H.$path_parts['filename'].".".$path_parts['extension'];
	} else {
	  $newFln = $directory.PRE_V.$path_parts['filename'].".".$path_parts['extension'];
	}
	if ( $renameFls == 1 ) {
		$res = @rename ( $image , $newFln );
		$action = !$res ? " Error while renaming with: " : " Renamed with: ";
	}
	if ( $renameFls == 2 ) {
		$res = @copy ( $image , $newFln );
		$action = !$res ? " Error while copying in: " : " Copied in: ";
	} 
	echo "File: ".$image." is ".$imgLayout.". ".$action.$newFln."\n";
}
 
?>

Un esempio di utilizzo in ambiente Linux rinominando i file jpg della directory “tmp” nella propria home (dove ho messo 10 file verticali e 10 orizzontali di prova):

~$ ./sortimages.php ~/tmp rename

e questo è l’output:


File: /home/mario/tmp/IMG_5546.JPG is horizontal. Renamed with: /home/mario/tmp/_H_IMG_5546.JPG
File: /home/mario/tmp/IMG_5547.JPG is vertical. Renamed with: /home/mario/tmp/_V_IMG_5547.JPG
File: /home/mario/tmp/IMG_5549.JPG is vertical. Renamed with: /home/mario/tmp/_V_IMG_5549.JPG
File: /home/mario/tmp/IMG_5551.JPG is horizontal. Renamed with: /home/mario/tmp/_H_IMG_5551.JPG
File: /home/mario/tmp/IMG_5552.JPG is vertical. Renamed with: /home/mario/tmp/_V_IMG_5552.JPG
File: /home/mario/tmp/IMG_5553.JPG is horizontal. Renamed with: /home/mario/tmp/_H_IMG_5553.JPG
File: /home/mario/tmp/IMG_5554.JPG is horizontal. Renamed with: /home/mario/tmp/_H_IMG_5554.JPG
File: /home/mario/tmp/IMG_5556.JPG is horizontal. Renamed with: /home/mario/tmp/_H_IMG_5556.JPG
File: /home/mario/tmp/IMG_5558.JPG is vertical. Renamed with: /home/mario/tmp/_V_IMG_5558.JPG
File: /home/mario/tmp/IMG_5559.JPG is horizontal. Renamed with: /home/mario/tmp/_H_IMG_5559.JPG
File: /home/mario/tmp/IMG_5560.JPG is horizontal. Renamed with: /home/mario/tmp/_H_IMG_5560.JPG
File: /home/mario/tmp/IMG_5561.JPG is vertical. Renamed with: /home/mario/tmp/_V_IMG_5561.JPG
File: /home/mario/tmp/IMG_5562.JPG is horizontal. Renamed with: /home/mario/tmp/_H_IMG_5562.JPG
File: /home/mario/tmp/IMG_5563.JPG is vertical. Renamed with: /home/mario/tmp/_V_IMG_5563.JPG
File: /home/mario/tmp/IMG_5564.JPG is vertical. Renamed with: /home/mario/tmp/_V_IMG_5564.JPG
File: /home/mario/tmp/IMG_5565.JPG is vertical. Renamed with: /home/mario/tmp/_V_IMG_5565.JPG
File: /home/mario/tmp/IMG_5566.JPG is vertical. Renamed with: /home/mario/tmp/_V_IMG_5566.JPG
File: /home/mario/tmp/IMG_5571.JPG is horizontal. Renamed with: /home/mario/tmp/_H_IMG_5571.JPG
File: /home/mario/tmp/IMG_5575.JPG is vertical. Renamed with: /home/mario/tmp/_V_IMG_5575.JPG
File: /home/mario/tmp/IMG_5576.JPG is horizontal. Renamed with: /home/mario/tmp/_H_IMG_5576.JPG

Potete scaricare il file in formato compresso .zip qui

WordPress: una classe PHP esterna per importare nei post da un array di dati

importWPHo scritto questa classe PHP per sopperire all’esigenza di importare dati provenienti da altri CMS in WordPress. Sebbene esistano diversi tool di importazione dai più diffusi CMS in WordPress, questi sono difficilmente adattabili a CMS proprietari oppure semplicemente per importare dati da archivi CSV.

Questa classe consente inoltre di creare, utilizzando semplicemente i post e le categorie di WordPress, un catalogo di prodotti che non abbia l’esigenza di un carrello della spesa.

La classe, sebbene includa tutte le funzioni di WordPress, è strutturata come elemento separato e non come plugin. Questo garantisce una maggior flessibilità di utilizzo e non lascia alcuna traccia una volta rimossa. E’ necessario però utilizzarla con accortezza e proteggere l’accesso agli script che la utilizzano, altrimenti chiunque potrebbe scrivere sul database.

Come si utilizza

Per un utilizzo semplificato è sufficiente avere un array contente per ciascuna riga solo il titolo del post e la descrizione. Facciamo un esempio:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require_once("ImportPostWp.class.php");
 
$myData = array();
 
for ($i=1;$i<11;$i++) { 	$myData[] = array('post_title'=>'Nuovo titolo n.'.$i,
                          'post_content'=>'Nuova descrizione del post '.$i
                         );
 }
$obj = new ImportPostWp($myData,"../wp");
$res = $obj->insert_posts();	//Inserisce nel DB tutti i posts dell'array utilizzando funzioni native di WP
echo "\n
Record inseriti: ".$res;
echo "\n
Errori: ".implode(",",$obj->get_insert_errors());
echo "\n
Tempo trascorso: ".$obj->get_elapsed_time();

Gli inserimenti con il metodo insert_posts() sono necessariamente più lenti perché il metodo utilizza le funzioni native di WordPress che fanno un bel po’ di controlli di sicurezza sui contenuti, ma assicurano sempre il corretto inserimento. Nel caso in cui i post da inserire siano in numero minore di 200-300 è il metodo consigliato.

Questo è l’elenco di tutti i campi che è possibile inserire nell’array:

‘post_title’,’post_content’,’post_name’, ‘post_status’,’post_type’,’guid’,’post_excerpt’, ‘post_date’,’post_date_gmt’,’ping_status’, ‘comment_status’,’post_author’,’categories’

Alcuni di questi hanno valori particolari, per esempio ‘guid’, ‘post_date_gmt’ devono essere inseriti con dei valori specifici come il permalink e la data di Greenwich, quindi nel caso si voglia inserirli nell’array, è necessario che l’utente sappia bene cosa sta facendo. La classe calcola automaticamente questi valori. Per una spiegazione dettagliata sull’utilizzo di questi campi si rimanda alla documentazione di WordPress: https://codex.wordpress.org/Function_Reference/wp_insert_post

Per inserimenti pesanti (>500) è consigliabile usare il metodo raw_insert_posts() che utilizza l’oggetto $wpdb per eseguire query grezze SQL. Questo metodo è infinitamente più veloce. Io l’ho utilizzato per inserire circa 6000 post, in poco più di un minuto. C’è da notare che questo metodo non esegue particolari controlli di sicurezza. Inoltre il guid (ovvero il permalink all’articolo) che viene generato è compatibile solo con le impostazioni generali del permalink di WordPress impostate su: “Nome articolo”.

Documentazione:

La documentazione della classe è disponibile sia nel pacchetto compresso che online qui: http://www.spadamar.com/files/ImportPostWp/docs/ImportPostWp/ImportPostWp.class.html

e qui: http://www.spadamar.com/files/ImportPostWp/ImportPostWp.html

è possibile vedere il codice sorgente.

Download:

Il pacchetto compresso zip è disponibile qui: ImportPostWp.zip

PHP cURL: una classe per controllare link interrotti, status code e nxdomain in parallelo

PHP cURL e status code
PHP cURL e status code

Questa classe nasce dall’esigenza di revisionare i numerosi bookmarks accumulati in anni di navigazione su Internet. Purtroppo uno degli inconvenienti di Internet è proprio la scarsa affidabilità sulla persistenza dei link. Succede spesso che i link collezionati anni prima non siano più attivi oppure siano stati ridirezionati su altri siti.

Questa classe utilizza le librerie cURL che nelle ultime versioni del PHP, sono state integrate nel pacchetto. Ho utilizzato in particolare la famiglia di comandi curl_multi* in modo da poter evadere richieste multiple parallelamente e velocizzare notevolmente il processo. Oltre a poter verificare le url, che vengono fornite al costruttore della classe come array, per particolari status code o per intere famiglie di status code, è possibile anche verificare che la risposta non sia un cosiddetto hit-nxdomain cioè un server che intercetta un nxdomain e propone un redirect ad una pagina di ricerca di domini dal nome simile. In pratica alcuni DNS (p.e. OpenDNS) in caso di dominio inesistente producono redirect pubblicitari attraverso i loro hit-nxdomain.

Leggi tutto

P4A 3 Framework: Gestione Fatture 2, un’applicazione completa

Gestione fatture 2

Ho deciso di riscrivere in larga parte il codice dell’applicazione Gestione Fatture che avevo pubblicato in questo articolo, per renderla compatibile con la nuova versione di P4A (disponibile qui) (la 3.8.4) ed anche per correggere diversi bug che sono emersi nell’utilizzo.

Pur mantenendo tutte le caratteristiche della precedente versione, l’attuale non è compatibile con quella precedente per via di alcune modifiche sostanziali che ho dovuto apportare alla struttura del database.

Oltre a poter essere utile proprio per la gestione delle fatture delle ditte individuale e dei professionisti, credo che questo codice possa essere utile anche a tutti coloro che si avvicinano al framework P4A, in quanto all’interno ci sono diversi esempi di come risolvere la maggior parte dei problemi che si incontrano sviluppando con questo framework.

Alcune delle nuove caratteristiche sono:

  • Riscritto lo schema del database (non è più compatibile con la versione precedente)
  • Possibilità di inserire il saldo o il saldo parziale riscosso della fattura emessa per il controllo dei pagamenti
  • Nuova funzionalità per monitorare le fatture saldate interamente o parzialmente (conteggio dei soldi in cassa e dell’importo da riscuotere)
  • Nuova funzionalità per il filtraggio delle fatture
  • Possibilità di emettere note di credito
  • Nuova funzionalità per fatture ricorrenti
  • Corretto il codice della classe ezPDF (i file sono allegati nella dir. Libraries) per la compatibilità con PHP 5.3.x
  • Possibilità di inserire automaticamente la descrizione di prestazioni già inserite in fatture precedenti

A chi è rivolto:

  • Soggetti senza P.IVA che rilasciano la sola ritenuta di acconto
  • Soggetti con P.IVA iscritti alla Camera di Commercio (Ditte individuali)
  • Professionisti senza Cassa previdenziale autonoma
  • Professionisti con Cassa previdenziale autonoma

Per la mancanza di una gestione del magazzino è sconsigliata ai commercianti che hanno a che fare con un magazzino merce, inventario e via dicendo.

L’applicazione viene distribuita con licenza LGPL 3, in questo link potete trovare la traduzione non ufficiale in italiano.

Installazione:

  • Installare il framework p4a 3.8.4 (requisito obbligatorio)
  • Scompattare l’archivio “gestionefatture2.zip” all’interno della directory “p4a/applications”
  •   Generare un database con il nome: “gestionefatture2” ed importare il dump SQL:  “gestionefatture2.sql” che si trova all’interno della directory “_private”
  • Modificare il file “index.php” sostituendo nella riga di codice:

define(“P4A_DSN”, ‘mysql://yourdbuser:yourdbpassword@localhost/gestionefatture2’);

yourdbuser = l’utente MySQL con il quale accedere al vostro database

yourdbpassword = la password del suddetto utente.

Infine per accedere all’applicazione i parametri di login sono:

Username = admin

Password = admin

E’ importante mantenere nella directory “/libraries” i file di libreria di ezPdf che sono contenuti nel pacchetto, perché ho dovuto correggere alcune linee di codice (purtroppo ezPdf è stata abbandonata dal suo autore…) per renderla compatibile con PHP 5.3.x.

La applicazione richiede che sia installato il PHP 5.3.x + MySQL 5.1.x o superiore.

DOWNLOAD: Gestione Fatture 2

Sono graditi commenti, suggerimenti e correzioni!

 

 

 

PHP: un socket TCP per acquisire dati dal GPS Tracker GPS-102 compatibile OpenGTS (terza parte)

php daemon
php daemon

In quest’ultimo articolo descrivo come si lancia in background lo script PHP su un server con sistema operativo Linux Ubuntu o Debian. Come vi sarete accorti lo script descritto nella seconda parte contiene nella prima riga il seguente codice:

#!/usr/bin/env php

in gergo questa sintassi si chiama Shebang e viene utilizzata per lanciare dalla shell l’interprete dello script che sta per essere eseguito. In questo caso il PHP. Sarà dunque necessario verificare che PHP5-CLI (command line interpreter) sia installato.

Leggi tutto