VestaCP import webu z jiného virtuálního serveru

Vytváříme skript, který exportuje web a databázi z jednoho Ubuntu Apache serveru na jiný Ubuntu VestaCP server a to téměř živě.

Zadání

Představme si dva servery, kdy jeden bude Ubutu Apache2 a druhý Ubuntu VestaCP. Chceme přesunout web vč. databáze z Apache2 na VestaCP, upravit config webu a nerušeně pokračovat v provozu na novém VestaCP serveru.

Postup v pěti bodech

  1. Najít na serveru Ubuntu Apache2 adresář dle doménového názvu a překopírovat jej přes ssh na server Ubuntu VestaCP
  2. Najít na serveru Ubuntu Apache2 databázi, exportovat ji a uložit ji přes ssh na Ubuntu VestaCP server.
  3. Z Ubuntu Apache2 serveru spustit přes ssh skript na Ubuntu VestaCP serveru, který importuje databázi a upraví config webu (nové údaje k databázi atp).
  4. Z Ubuntu Apache2 serveru spustit přes ssh skript na Proxy serveru a přesměrovat doménu na VestaCP server.
  5. Na Ubuntu Apache2 server nahrát dočasný index, upozorňující na přesun webu (neměl by se uživateli zobrazit).

Existující úpravy

Protože jsme trošku líní už dříve jsme propojili VestaCP s vytvořeným FreeNas serverem a nasdíleli si nějaké ty windowsovské složky – více v návodu: Propojení VestaCP a FreeNas (přesuny souborů a skriptů).

Existují tedy tyto tři adresáře:

  • /d2a-sdilene-skripty – složka na FreeNasu (virtuálního na Proxmoxu) sdílená a propojená na VestaCP
  • /d2a-skripty – lokální složka pro věci, které musí šlapat i když bude nedostupný FreeNas
  • /d2a-zalohy-nas01 – složka FreeNasu (fyzického uložiště) sdílená a propojená na VestaCP

Ve skriptu jsou dále zmíněny adresáře /xD2A/xNAS//xD2A/xBASH/. Jsou to další sdílený adresář z Nasu, používaný ale pouze na starých serverech ze kterých nyní migrujeme.

Skript pro Ubuntu Apache2

Zakládáme soubor.

nano /xD2A/xBASH/export-webu/export-webu.bash
#!/bin/bash

# mazu sklo
clear

# export-webu
#
#   Sublime Text nastaveni
#     Tabsize: 2
#     Viev -> Line endings -> UNIX
#   Nastaveni prav pro spusteni
#     chmod u+x /xD2A/xBASH/export-webu/export-webu.bash
#   Pouziti skriptu
#     /xD2A/xBASH/export-webu/export-webu.bash domena='ex1.d2a.cz'


#######################################################################
#
#
# Nastavovaci promenne
#
#
#######################################################################

# vystup - urcuje kam se ma zapisovat prubeh skriptu (kriticke chyby se zapisuji vzdy)
#       na-sklo
#       do-logu
#       vsude
#       nikde
vystup="vsude"

# adresar pro exporty - nesmi obsahovat lomitko na konci
dir_export="/xD2A/xNAS/export_v1"

# pristupy do zdrojove databaze
zdroj_db_jm="root"
zdroj_db_hes="HESLO"

#######################################################################
#
#
# Staticke promenne - neupravovat
#
#
#######################################################################

# nazev skriptu (udava se bez .bash)
nazev_skriptu="export-webu"

# systemova nastaveni barev výstupu na sklo
c="\033[31m"
z="\033[32m"
tr="\033[0m"

# koren_logu - urcuje zakladni koren pro ukladani logu skriptu (nesmi obsahovat lomitko na konci)
#   default: /xD2A/xBASH/export-webu/log 
koren_logu="/xD2A/xBASH/export-webu/log"

# razitko datumu a casu
cas_raz=$(date '+%Y%m%d_%H%M%S_%N')
rok=$(date '+%Y')
mesic=$(date '+%m')
den=$(date '+%d')
hodina=$(date '+%H')

# jmeno a ip adresa masiny na ktere je spusteno
jmeno_host=$(hostname)
ip_host=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')

# cesta logu (nesmi obsahovat lomitko na konci)
adresar_logu="${koren_logu}/${nazev_skriptu}/${rok}"
soubor_logu="${adresar_logu}/${jmeno_host}_T${cas_raz}_IP${ip_host//./_}.log"

# docasny adresar pro odkladani souboru (nesmi obsahovat lomitko na konci)
docasny_adresar="/tmp/${nazev_skriptu}"

# prednastaveni ziskavanych promennych
domena="n"

# ziskani vstupu
for ARGUMENT in "$@"
do
	KEY=$(echo $ARGUMENT | cut -f1 -d=)
	VALUE=$(echo $ARGUMENT | cut -f2 -d=)   
	case "$KEY" in
		domena)                 domena=${VALUE} ;;
	*)   
	esac    
done

#######################################################################
#
#
# funkce vypisu na sklo / do logu / oboje
#
#
#######################################################################

function _ {
	cas="$(date '+%H:%M:%S')\t"
	if [ "${vystup}" == "na-sklo" ] || [ "${vystup}" == "vsude" ]; then
		printf "${cas}$* \n";
	fi
	if [ "${vystup}" == "do-logu" ] || [ "${vystup}" == "vsude" ]; then
		echo -e "${cas}$*" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" >> "${soubor_logu}"
	fi  
}

#######################################################################
#
#
# funkce vytvoreni adresare pro logovani souboru
#
#
#######################################################################

function priprav_log_dir {
	max_pocet_pokusu=5
	prodleva_mezi_pokusy="1m"
	pocet_selhani=0
	for (( _i=1; _i<=max_pocet_pokusu; _i++ ))
	do
		printf "Overuji adresar logu ${1} (pokus ${_i} z ${max_pocet_pokusu}).\n"
		if [ -d "${1}" ]; then
			printf "${z}Ok:${tr} Adresar pro logovani existuje.\n"
			break
		else
			printf "${c}Er:${tr} Adresar pro logovani neexistuje.\n"
			printf "Cekam ${prodleva_mezi_pokusy}.\n"
			sleep "${prodleva_mezi_pokusy}"
			printf "Zahajuji pokus o vytvoreni adresare.\n"
			mkdir -p "${1}"
			pocet_selhani=${_i}
		fi
	done
	if [ ${pocet_selhani} = ${max_pocet_pokusu}  ]; then
		printf "${c}Chyba:${tr} Nepodarilo se vytvorit adresar logu. Lze ocekavat dalsi chyby. Koncim skript.\n"
		exit 1
	fi
}

#######################################################################
#
#
# funkce testovani promennych
# kriticka_promenna [nazev promenne] [zakazana hodnota] [chybova hlaska]
#
#
#######################################################################

function kriticka_promenna {
	promenna=$1
	zakazana_hodnota=$2
	chybova_hlaska=$3
	# promenna nenastavena
	if [ "${!promenna}" == "${zakazana_hodnota}" ]; then
		_ "${c}Chyba:${tr} Promenna ${promenna} nenastavena. ${chybova_hlaska} | Koncim skript."
		exit 1
	fi
}

#######################################################################
#
#
# funkce testovani promennych
# doporucena_promenna [nazev promenne] [zakazana hodnota] [chybova hlaska]
#
#
#######################################################################

function doporucena_promenna {
	promenna=$1
	zakazana_hodnota=$2
	chybova_hlaska=$3
	# promenna nenastavena
	if [ "${!promenna}" == "${zakazana_hodnota}" ]; then
		_ "${c}Upozorneni:${tr} Promenna ${promenna} nenastavena. ${chybova_hlaska} | Skript pokracuje."
	fi
}

#######################################################################
#
#
# pripravne prace
#
#
#######################################################################

# doinstalace baliku, ktere mohou chybet
# if ! dpkg-query -W -f='${Status}' sshpass | grep "ok installed"; then apt-get install sshpass -y; fi

# pokus o vytvoreni adresare pro log
priprav_log_dir ${adresar_logu}

#######################################################################
#
#
# zahajeni skriptu
#
#
#######################################################################

_ "${z}Spoustim skript ${nazev_skriptu}.bash.${tr}"
_ ""
_ "Uziti: export-webu.bash domena='domena'"
_ ""
_ "Vypisuji zadani:"
_ "  domena: ${domena}"
_ ""
_ "Zahajuji kontroly"

kriticka_promenna "domena" "n" ""

_ "Kontroly dokonceny"
_ ""

# sestavuji cesty adresaru - nesmi obsahovat lomitko na konci
adr_zdrojovy="/www/doc/${domena}/www"
adr_cilovy="${dir_export}/${domena}"
_ "Adresare:"
_ "  adr_zdrojovy: ${adr_zdrojovy} (tato masina)"
_ "  adr_cilovy: ${adr_cilovy}"
_ ""

#######################################################################
#
#
# hlavni cast skriptu
#
#
#######################################################################

_ "Kontroluji existenci adr_zdrojovy."
if [ ! -d "${adr_zdrojovy}/" ]; then
	_ "${c}Chyba:${tr} Adresar adr_zdrojovy neexistuje. | Koncim skript."
	exit 1
fi
_ ""

_ "Mazu slozku exportu adr_cilovy."
_ "$(rm -R ${adr_cilovy} 2>>1)"
sleep 10

_ "Vytvarim prazdnou slozku exportu adr_cilovy."
_ "$(mkdir -p ${adr_cilovy} 2>>1)"
_ ""
sleep 2

_ "Kontroluji existenci adr_cilovy."
if [ ! -d "${adr_cilovy}/" ]; then
	_ "${c}Chyba:${tr} Adresar adr_cilovy neexistuje. | Koncim skript."
	exit 1
fi
_ ""

_ "Kontroluji databazi."
nazev_db=${domena//./_}
nazev_db=${nazev_db//-/_}
zdroj_db_exist=`mysqlshow --user=${zdroj_db_jm} --password=${zdroj_db_hes} ${nazev_db} | grep -v Wildcard | grep -o ${nazev_db}`
if [ ! "$zdroj_db_exist" == ${nazev_db} ]; then
	_ "${c}Chyba:${tr} Ke zdrojove databazi ${nazev_db} se nelze pripojit. | Koncim skript."
	exit 1	
fi
_ ""

_ "Kopiruji data"
_ "$(cp -R "${adr_zdrojovy}/." "${adr_cilovy}/" 2>>1)"

_ "Exportuji DB."
mysqldump --user=${zdroj_db_jm} --password=${zdroj_db_hes} ${nazev_db} > "${adr_cilovy}/.db-export-v1.sql"

#######################################################################
#
#
# exit
#
#
#######################################################################

_ "${z}Koncim skript${tr}"
exit 0
;;

Na sdíleném uložišti teď máme komplet web včetně DB.

Skript pro Ubuntu VestaCP

nano /d2a-sdilene-skripty/importuj-a-uprav-wordpress-z-nasu.bash

Samotný skript:

#!/bin/bash

# mazu sklo
clear

# importuj-a-uprav-wordpress-z-nasu
#
#   Sublime Text nastaveni
#     Tabsize: 2
#     Viev -> Line endings -> UNIX
#   Nastaveni prav pro spusteni
#     chmod u+x /d2a-sdilene-skripty/importuj-a-uprav-wordpress-z-nasu.bash
#   Pouziti skriptu
#     /d2a-sdilene-skripty/importuj-a-uprav-wordpress-z-nasu.bash uzivatel='UZIVATEL' domena='DOMENA' db_nazev='NAZEV' db_jm='JMENO' db_hes='HESLO'


#######################################################################
#
#
# Nastavovaci promenne
#
#
#######################################################################

# vystup - urcuje kam se ma zapisovat prubeh skriptu (kriticke chyby se zapisuji vzdy)
#       na-sklo
#       do-logu
#       vsude
#       nikde
vystup="vsude"

# adresar ze ktereho se bude importovat - nesmi obsahovat lomitko na konci
dir_import="/d2a-zalohy-nas01/export_v1"

# DB host
db_host="localhost"

#######################################################################
#
#
# Staticke promenne - neupravovat
#
#
#######################################################################

# nazev skriptu (udava se bez .bash)
nazev_skriptu="importuj-a-uprav-wordpress-z-nasu"

# systemova nastaveni barev výstupu na sklo
c="\033[31m"
z="\033[32m"
tr="\033[0m"

# razitko datumu a casu
cas_raz=$(date '+%Y%m%d_%H%M%S_%N')
rok=$(date '+%Y')
mesic=$(date '+%m')
den=$(date '+%d')
hodina=$(date '+%H')

# jmeno a ip adresa masiny na ktere je spusteno
jmeno_host=$(hostname)
ip_host=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')

# docasny adresar pro odkladani souboru (nesmi obsahovat lomitko na konci)
adresar_tmp="/tmp/${nazev_skriptu}"

# koren_logu - urcuje zakladni koren pro ukladani logu skriptu (nesmi obsahovat lomitko na konci)
#   default: /d2a-sdilene-skripty/importuj-a-uprav-wordpress-z-nasu/log 
koren_logu="/d2a-sdilene-skripty/log"

# cesta logu (nesmi obsahovat lomitko na konci)
adresar_logu="${koren_logu}/${nazev_skriptu}/${rok}"
soubor_logu="${adresar_logu}/${jmeno_host}_T${cas_raz}_IP${ip_host//./_}.log"

# koren data pro tento skript (nesmi obsahovat lomitko na konci)
#		default: /d2a-sdilene-skripty/data
koren_data="/d2a-sdilene-skripty/data"

# adresar data (nesmi obsahovat lomitko na konci)
adresar_data="${koren_data}/${nazev_skriptu}"

# prednastaveni ziskavanych promennych
domena="n"
uzivatel="n"
db_nazev="n"
db_jm="n"
db_hes="n"

# ziskani vstupu
for ARGUMENT in "$@"
do
	KEY=$(echo $ARGUMENT | cut -f1 -d=)
	VALUE=$(echo $ARGUMENT | cut -f2 -d=)   
	case "$KEY" in
		uzivatel)               uzivatel=${VALUE} ;;
		domena)                 domena=${VALUE} ;;
		db_nazev)               db_nazev=${VALUE} ;;
		db_jm)                 	db_jm=${VALUE} ;;
		db_hes)                 db_hes=${VALUE} ;;
	*)   
	esac    
done

#######################################################################
#
#
# funkce vypisu na sklo / do logu / oboje
#
#
#######################################################################

function _ {
	cas="$(date '+%H:%M:%S')\t"
	if [ "${vystup}" == "na-sklo" ] || [ "${vystup}" == "vsude" ]; then
		printf "${cas}$* \n";
	fi
	if [ "${vystup}" == "do-logu" ] || [ "${vystup}" == "vsude" ]; then
		echo -e "${cas}$*" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" >> "${soubor_logu}"
	fi  
}

#######################################################################
#
#
# funkce vytvoreni adresare pro logovani souboru
#
#
#######################################################################

function priprav_log_dir {
	max_pocet_pokusu=5
	prodleva_mezi_pokusy="1m"
	pocet_selhani=0
	for (( _i=1; _i<=max_pocet_pokusu; _i++ ))
	do
		printf "Overuji adresar logu ${1} (pokus ${_i} z ${max_pocet_pokusu}).\n"
		if [ -d "${1}" ]; then
			printf "${z}Ok:${tr} Adresar pro logovani existuje.\n"
			break
		else
			printf "${c}Er:${tr} Adresar pro logovani neexistuje.\n"
			printf "Cekam ${prodleva_mezi_pokusy}.\n"
			sleep "${prodleva_mezi_pokusy}"
			printf "Zahajuji pokus o vytvoreni adresare.\n"
			mkdir -p "${1}"
			pocet_selhani=${_i}
		fi
	done
	if [ ${pocet_selhani} = ${max_pocet_pokusu}  ]; then
		printf "${c}Chyba:${tr} Nepodarilo se vytvorit adresar logu. Lze ocekavat dalsi chyby. Koncim skript.\n"
		exit 1
	fi
}

#######################################################################
#
#
# funkce testovani promennych
# kriticka_promenna [nazev promenne] [zakazana hodnota] [chybova hlaska]
#
#
#######################################################################

function kriticka_promenna {
	promenna=$1
	zakazana_hodnota=$2
	chybova_hlaska=$3
	# promenna nenastavena
	if [ "${!promenna}" == "${zakazana_hodnota}" ]; then
		_ "${c}Chyba:${tr} Promenna ${promenna} nenastavena. ${chybova_hlaska} | Koncim skript."
		exit 1
	fi
}

#######################################################################
#
#
# funkce testovani promennych
# doporucena_promenna [nazev promenne] [zakazana hodnota] [chybova hlaska]
#
#
#######################################################################

function doporucena_promenna {
	promenna=$1
	zakazana_hodnota=$2
	chybova_hlaska=$3
	# promenna nenastavena
	if [ "${!promenna}" == "${zakazana_hodnota}" ]; then
		_ "${c}Upozorneni:${tr} Promenna ${promenna} nenastavena. ${chybova_hlaska} | Skript pokracuje."
	fi
}

#######################################################################
#
#
# pripravne prace
#
#
#######################################################################

# doinstalace baliku, ktere mohou chybet
# if ! dpkg-query -W -f='${Status}' sshpass | grep "ok installed"; then apt-get install sshpass -y; fi

# pokus o vytvoreni adresare pro log
priprav_log_dir ${adresar_logu}

#######################################################################
#
#
# zahajeni skriptu
#
#
#######################################################################

_ "${z}Spoustim skript ${nazev_skriptu}.bash.${tr}"
_ ""
_ "Uziti: importuj-a-uprav-wordpress-z-nasu.bash uzivatel='uzivatel' domena='domena'"
_ ""
_ "Vypisuji zadani:"
_ "  uzivatel: ${uzivatel}"
_ "  domena: ${domena}"
_ "  db_nazev: ${db_nazev}"
_ "  db_jm: ${db_jm}"
_ "  db_hes: ****"
_ ""
_ "Zahajuji kontroly"

kriticka_promenna "uzivatel" "n" ""
kriticka_promenna "domena" "n" ""
kriticka_promenna "db_nazev" "n" ""
kriticka_promenna "db_jm" "n" ""
kriticka_promenna "db_hes" "n" ""

_ "Kontroly dokonceny"
_ ""

# sestavuji cesty adresaru - nesmi obsahovat lomitko na konci
adr_zdrojovy="${dir_import}/${domena}"
adr_cilovy="/home/${uzivatel}/web/${domena}/public_shtml"
_ "Adresare:"
_ "  adr_zdrojovy: ${adr_zdrojovy} (tato masina)"
_ "  adr_cilovy: ${adr_cilovy}"
_ ""

#######################################################################
#
#
# hlavni cast skriptu
#
#
#######################################################################

_ "Kontroluji existenci adr_zdrojovy."
if [ ! -d "${adr_zdrojovy}/" ]; then
	_ "${c}Chyba:${tr} Adresar adr_zdrojovy neexistuje. | Koncim skript."
	exit 1
fi
_ ""

_ "Kontroluji existenci adr_cilovy."
if [ ! -d "${adr_cilovy}/" ]; then
	_ "${c}Chyba:${tr} Adresar adr_cilovy neexistuje. | Koncim skript."
	exit 1
fi
_ ""

_ "Kopiruji data."
_ "$(cp -R "${adr_zdrojovy}/." "${adr_cilovy}/" 2>>1)"

_ "Importuji DB."
mysql -u "${db_jm}" -p"${db_hes}" "${db_nazev}" < "${adr_cilovy}/.db-export-v1.sql"

_ "Kopiruji nenaplneny wp-config.php.vzor."
cp -a "${adresar_data}/wp-config.php.vzor" "${adr_cilovy}/wp-config.php.vzor"

_ "Zjistuji puvodni prefix."
db_prefix=$(grep -hnr "table_prefix" "${adr_cilovy}/wp-config.php" | cut -f2 -d"'")

_ "Generuji klice"
function gen_klic {
	local a=$((tr -cd "a-zA-Z0-9" < /dev/urandom | head -c 64) 2>/dev/null)
	a=${a/1/'_'}
	a=${a/2/'-'}
	a=${a/3/'+'}
	a=${a/4/'.'}
	a=${a/5/' '}
	a=${a/6/'!'}
	a=${a/7/':'}
	echo "$a"
}

function sed_cesta { 
	local cesta=$((echo $1 | sed -r 's/([\$\.\*\/\[\\^])/\\\1/g' | sed 's/[]]/\[]]/g')>&amp;1)
	echo "$cesta"
}

_ "Upravuji wp-config.php.vzor."
sed -i "s/___DB_NAZEV___/${db_nazev}/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___DB_JM___/${db_jm}/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___DB_HES___/${db_hes}/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___DB_HOST___/${db_host}/g" "${adr_cilovy}/wp-config.php.vzor"

sed -i "s/___KLIC_1___/$(gen_klic)/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___KLIC_2___/$(gen_klic)/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___KLIC_3___/$(gen_klic)/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___KLIC_4___/$(gen_klic)/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___KLIC_5___/$(gen_klic)/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___KLIC_6___/$(gen_klic)/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___KLIC_7___/$(gen_klic)/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___KLIC_8___/$(gen_klic)/g" "${adr_cilovy}/wp-config.php.vzor"

sed -i "s/___PREFIX_TABULEK___/${db_prefix}/g" "${adr_cilovy}/wp-config.php.vzor"
sed -i "s/___DOMENA___/${domena}/g" "${adr_cilovy}/wp-config.php.vzor"

_ "Mazu puvodni wp-config.php."
rm -R "${adr_cilovy}/wp-config.php"

_ "Prejmenovavam wp-config.php.vzor."
mv "${adr_cilovy}/wp-config.php.vzor" "${adr_cilovy}/wp-config.php"

_ "Mazu puvodni .db-export-v1.sql"
rm -R "${adr_cilovy}/.db-export-v1.sql"

_ "Mazu puvodni index.html"
rm -R "${adr_cilovy}/index.html"


#######################################################################
#
#
# vzdy posledni krok !
#
#
#######################################################################

_ "Upraviji prava."
#
# This script configures WordPress file permissions based on recommendations
# from http://codex.wordpress.org/Hardening_WordPress#File_permissions
#
# Author: Michael Conigliaro
#
WP_OWNER="${uzivatel}" # -- wordpress owner
WP_GROUP="${uzivatel}" # -- wordpress group
WP_ROOT="${adr_cilovy}" # -- wordpress root directory
WS_GROUP="${uzivatel}" # -- webserver group

# reset to safe defaults
find ${WP_ROOT} -exec chown ${WP_OWNER}:${WP_GROUP} {} \;
find ${WP_ROOT} -type d -exec chmod 755 {} \;
find ${WP_ROOT} -type f -exec chmod 644 {} \;

# allow wordpress to manage wp-config.php (but prevent world access)
chgrp ${WS_GROUP} ${WP_ROOT}/wp-config.php
chmod 660 ${WP_ROOT}/wp-config.php

# allow wordpress to manage .htaccess
touch ${WP_ROOT}/.htaccess
chgrp ${WS_GROUP} ${WP_ROOT}/.htaccess
chmod 664 ${WP_ROOT}/.htaccess

# allow wordpress to manage wp-content
find ${WP_ROOT}/wp-content -exec chgrp ${WS_GROUP} {} \;
find ${WP_ROOT}/wp-content -type d -exec chmod 775 {} \;
find ${WP_ROOT}/wp-content -type f -exec chmod 664 {} \;

#######################################################################
#
#
# exit
#
#
#######################################################################

_ "${z}Koncim skript${tr}"
exit 0
;;

Hotovo

Tyto skripty opět voláme hromadně s parametry nagenerovanými z tabulky schraňující nastavení všech webů.

Martin Horák

Martin Horák má na svědomí 29 příspěvků

Již nějakou chvíli se věnuji tvorbě webů, copywritingu a všem těm drobným a nepodstatným věcem okolo toho.

Najmout si mě můžete přes horní lištu a klikátko kontakty.

Diskuze

Vaše emailová adresa nebude publikována. Povinné údaje jsou označeny *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>