Ver Fonte

Merge branch 'INTERNAL'

Iñigo Valentin há 8 anos atrás
pai
commit
4e7d046a71
100 ficheiros alterados com 5303 adições e 312 exclusões
  1. 3 0
      .gitignore
  2. 53 12
      sql/00_STRUCTURE.SQL
  3. 34 0
      sql/01_PERMISSIONS.SQL
  4. 349 0
      sql/02_DATA.SQL
  5. 7 0
      sql/README.md
  6. 8 0
      sql/__SETUP.SQL
  7. 0 17
      sql/dbusers-example.sql
  8. 0 30
      sql/text.sql
  9. 24 0
      utils/imagescaler
  10. 68 0
      www-admin/authentication.php
  11. 31 0
      www-admin/blog/delete.php
  12. 28 0
      www-admin/blog/deletecomment.php
  13. 295 0
      www-admin/blog/edit.php
  14. 149 0
      www-admin/blog/index.php
  15. 20 0
      www-admin/blog/new.php
  16. 28 0
      www-admin/blog/uploadimage.php
  17. 29 0
      www-admin/blog/visible.php
  18. 131 0
      www-admin/css/blog.css
  19. 29 0
      www-admin/css/home.css
  20. 0 0
      www-admin/css/m/ui.css
  21. 169 0
      www-admin/css/projects.css
  22. 5 0
      www-admin/css/stats.css
  23. 507 0
      www-admin/css/ui.css
  24. BIN
      www-admin/fonts/Nunito-Bold.ttf
  25. BIN
      www-admin/fonts/Nunito-Light.ttf
  26. BIN
      www-admin/fonts/Nunito-Regular.ttf
  27. 93 0
      www-admin/fonts/OFL.txt
  28. 354 0
      www-admin/functions.php
  29. 37 0
      www-admin/functions/save.php
  30. 87 0
      www-admin/header.php
  31. BIN
      www-admin/img/icon/cancel.png
  32. BIN
      www-admin/img/icon/edit.png
  33. BIN
      www-admin/img/icon/save.png
  34. 101 0
      www-admin/index.php
  35. 14 0
      www-admin/login.php
  36. 33 0
      www-admin/projects/delete.php
  37. 28 0
      www-admin/projects/deletecomment.php
  38. 28 0
      www-admin/projects/deleteversion.php
  39. 421 0
      www-admin/projects/edit.php
  40. 161 0
      www-admin/projects/index.php
  41. 20 0
      www-admin/projects/new.php
  42. 28 0
      www-admin/projects/uploadimage.php
  43. 29 0
      www-admin/projects/visible.php
  44. 74 0
      www-admin/script/blog.js
  45. 136 0
      www-admin/script/projects.js
  46. 227 0
      www-admin/script/stats.js
  47. 204 0
      www-admin/script/ui.js
  48. 0 0
      www-admin/settings/index.php
  49. 0 0
      www-admin/settings/profile.php
  50. 269 0
      www-admin/stats/index.php
  51. 125 0
      www/.htaccess
  52. 152 20
      www/blog/index.php
  53. 8 7
      www/blog/post.php
  54. 0 72
      www/contact/index.php
  55. 62 0
      www/css/blog.css
  56. 22 0
      www/css/home.css
  57. 0 0
      www/css/m/blog.css
  58. 0 0
      www/css/m/profile.css
  59. 0 0
      www/css/m/project.css
  60. 58 0
      www/css/profile.css
  61. 106 0
      www/css/project.css
  62. 79 39
      www/css/ui.css
  63. 0 72
      www/cv/index.php
  64. 101 0
      www/error/403.php
  65. 101 0
      www/error/404.php
  66. 101 0
      www/error/500.php
  67. 17 7
      www/footer.php
  68. 44 21
      www/functions.php
  69. 9 9
      www/header.php
  70. 7 6
      www/help/index.php
  71. 0 0
      www/img/icon/profile.gif
  72. BIN
      www/img/icon/project.gif
  73. BIN
      www/img/icon/x1/blog.gif
  74. BIN
      www/img/icon/x1/home.gif
  75. BIN
      www/img/icon/x1/profile.gif
  76. BIN
      www/img/icon/x1/project.gif
  77. BIN
      www/img/icon/x1/projects.gif
  78. BIN
      www/img/icon/x2/blog.gif
  79. BIN
      www/img/icon/x2/home.gif
  80. BIN
      www/img/icon/x2/profile.gif
  81. BIN
      www/img/icon/x2/project.gif
  82. BIN
      www/img/icon/x2/projects.gif
  83. BIN
      www/img/icon/x3/blog.gif
  84. BIN
      www/img/icon/x3/home.gif
  85. BIN
      www/img/icon/x3/profile.gif
  86. BIN
      www/img/icon/x3/project.gif
  87. BIN
      www/img/icon/x3/projects.gif
  88. BIN
      www/img/icon/x4/blog.gif
  89. BIN
      www/img/icon/x4/home.gif
  90. BIN
      www/img/icon/x4/profile.gif
  91. BIN
      www/img/icon/x4/project.gif
  92. BIN
      www/img/icon/x4/projects.gif
  93. BIN
      www/img/icon/x5/blog.gif
  94. BIN
      www/img/icon/x5/home.gif
  95. BIN
      www/img/icon/x5/profile.gif
  96. BIN
      www/img/icon/x5/project.gif
  97. BIN
      www/img/icon/x5/projects.gif
  98. BIN
      www/img/icon/x6/blog.gif
  99. BIN
      www/img/icon/x6/home.gif
  100. BIN
      www/img/icon/x6/profile.gif

+ 3 - 0
.gitignore

@@ -23,6 +23,7 @@ www-admin/redirect_s.php
 #SQL data
 sql/dbusers.sql
 sql/*row*.sql
+sql/CUSTOM_ROWS.SQL
 
 #Passwords file
 sql/dbpasswords.sql
@@ -31,3 +32,5 @@ www-admin/.htpasswd
 www/.htaccess_local
 www-admin/.htaccess_local
 
+#3rd party software
+www-admin/script/ckeditor

+ 53 - 12
sql/setup.sql → sql/00_STRUCTURE.SQL

@@ -15,7 +15,8 @@ CREATE TABLE text (
     id       VARCHAR(32),
     lang     VARCHAR(2)     REFERENCES lang.code,
     section  VARCHAR(32),
-    text     VARCHAR(5000)  NOT NULL,
+    text     VARCHAR(5000),
+    file     VARCHAR(32),
     PRIMARY KEY(id, lang)
 );
 CREATE INDEX i_text ON text(id, lang);
@@ -47,6 +48,15 @@ CREATE TABLE permissions(
     PRIMARY KEY(user, permission)
 );
 
+CREATE TABLE social(
+    id       INT           AUTO_INCREMENT  PRIMARY KEY,
+    idx      INT           NOT NULL        UNIQUE,
+    visible  BOOLEAN       NOT NULL        DEFAULT 1,
+    title    VARCHAR(32)   NOT NULL        REFERENCES text.id,
+    icon     VARCHAR(16)   NOT NULL,
+    url      VARCHAR(256)  NOT NULL
+);
+
 CREATE TABLE post (
     id         INT           AUTO_INCREMENT  PRIMARY KEY,
     permalink  VARCHAR(300)  NOT NULL,
@@ -83,6 +93,14 @@ CREATE TABLE post_comment (
     visit     INT            REFERENCES stat_visit.id
 );
 
+CREATE TABLE license (
+    id       VARCHAR(32)  PRIMARY KEY,
+    summary  VARCHAR(32)  NOT NULL     REFERENCES text.id,
+    legal    VARCHAR(32)  NOT NULL     REFERENCES text.id,
+    logo     VARCHAR(32),
+    icon     VARCHAR(32)
+);
+
 CREATE TABLE project_type (
     id       VARCHAR(1)   NOT NULL  PRIMARY KEY,
     title    VARCHAR(32)  NOT NULL  REFERENCES text.id,
@@ -90,16 +108,17 @@ CREATE TABLE project_type (
 );
 
 CREATE TABLE project (
-    id         INT           AUTO_INCREMENT  PRIMARY KEY,
+    id         INT           AUTO_INCREMENT         PRIMARY KEY,
     permalink  VARCHAR(300)  NOT NULL,
-    type       VARCHAR(1)    NOT NULL        REFERENCES project_type.id,
-    title      VARCHAR(32)   NOT NULL        REFERENCES text.id,
+    type       VARCHAR(1)    NOT NULL               REFERENCES project_type.id,
+    title      VARCHAR(32)   NOT NULL               REFERENCES text.id,
     logo       VARCHAR(32),
-    header     VARCHAR(32)   NOT NULL        REFERENCES text.id,
-    text       VARCHAR(32)   NOT NULL        REFERENCES text.id,
-    user       INT           NOT NULL        REFERENCES user.id,
-    visible    BOOLEAN       NOT NULL        DEFAULT 1,
-    comments   BOOLEAN       NOT NULL        DEFAULT 1
+    header     VARCHAR(32)   REFERENCES text.id,
+    text       VARCHAR(32)   REFERENCES text.id,
+    license    VARCHAR(32)   REFERENCES license.id,
+    user       INT           NOT NULL               REFERENCES user.id,
+    visible    BOOLEAN       NOT NULL               DEFAULT 1,
+    comments   BOOLEAN       NOT NULL               DEFAULT 1
 );
 
 CREATE TABLE project_version_type (
@@ -122,9 +141,10 @@ CREATE TABLE project_version (
 );
 
 CREATE TABLE project_url_type (
-    id       VARCHAR(1)   NOT NULL  PRIMARY KEY,
-    title    VARCHAR(32)  NOT NULL  REFERENCES text.id,
-    summary  VARCHAR(32)  NOT NULL  REFERENCES text.id
+    id       VARCHAR(1)   NOT NULL            PRIMARY KEY,
+    title    VARCHAR(32)  REFERENCES text.id,
+    summary  VARCHAR(32)  REFERENCES text.id,
+    logo     VARCHAR(32)
 );
 
 CREATE TABLE project_url (
@@ -160,6 +180,27 @@ CREATE TABLE project_comment (
     visit     INT            REFERENCES stat_visit.id
 );
 
+CREATE TABLE cv_category (
+    id        INT          AUTO_INCREMENT      PRIMARY KEY,
+    title     VARCHAR(32)  REFERENCES text.id,
+    summary   VARCHAR(32)  REFERENCES text.id,
+    visible   BOOLEAN      NOT NULL            DEFAULT 1,
+    priority  INT          NOT NULL            UNIQUE
+);
+
+CREATE TABLE cv_entry (
+    id               INT          AUTO_INCREMENT  PRIMARY KEY,
+    category         INT          NOT NULL        REFERENCES cv_category.id,
+    role             VARCHAR(32)  NOT NULL        REFERENCES text.id,
+    company          VARCHAR(64)  NOT NULL,
+    city             VARCHAR(32)  NOT NULL        REFERENCES text.id,
+    start            DATETIME     NOT NULL,
+    end              DATETIME,
+    date_precission  VARCHAR(1)   NOT NULL        DEFAULT 'M',
+    summary          VARCHAR(32)  NOT NULL        REFERENCES text.id,
+    visible          BOOLEAN      NOT NULL        DEFAULT 1
+);
+
 CREATE TABLE settings (
     name     VARCHAR(64)  PRIMARY KEY,
     value    VARCHAR(64)  NOT NULL,

+ 34 - 0
sql/01_PERMISSIONS.SQL

@@ -0,0 +1,34 @@
+-- Replace all instances of XXX with the external site db password (same one as in www/.htaccess).
+-- Uncomment all entries.
+
+--GRANT SELECT ON         iv.cv_category          TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.cv_entry             TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.lang                 TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.license              TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.text                 TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.user                 TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.post                 TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.post_tag             TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.post_image           TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT, INSERT ON iv.post_comment         TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.project              TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.project_type         TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.project_tag          TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.project_url          TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.project_url_type     TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.project_image        TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.project_tag          TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.project_version      TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.project_version_type TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT, INSERT ON iv.project_comment      TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.settings             TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+--GRANT SELECT ON         iv.social               TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
+
+
+
+
+
+-- Replace YYY with the internal site db password (same one as in www-admin/.htaccess).
+-- Uncomment entry.
+
+--GRANT SELECT, INSERT, DELETE, UPDATE, SHOW VIEW ON iv.* TO 'ivint'@'localhost' IDENTIFIED BY 'YYY';

+ 349 - 0
sql/02_DATA.SQL

@@ -0,0 +1,349 @@
+-- AVAILABLE LANGUAGES
+INSERT INTO lang VALUES ('es', 'Castellano', '1');
+INSERT INTO lang VALUES ('en', 'English', '1');
+INSERT INTO lang VALUES ('eu', 'Euskara', '1');
+
+-- DATE ELEMENTS
+INSERT INTO text VALUES ('MONTH_01', 'es', 'DATE', 'enero', null);
+INSERT INTO text VALUES ('MONTH_01', 'en', 'DATE', 'January', null);
+INSERT INTO text VALUES ('MONTH_01', 'eu', 'DATE', 'urtarrila', null);
+INSERT INTO text VALUES ('MONTH_02', 'es', 'DATE', 'febrero', null);
+INSERT INTO text VALUES ('MONTH_02', 'en', 'DATE', 'February', null);
+INSERT INTO text VALUES ('MONTH_02', 'eu', 'DATE', 'otsaila', null);
+INSERT INTO text VALUES ('MONTH_03', 'es', 'DATE', 'marzo', null);
+INSERT INTO text VALUES ('MONTH_03', 'en', 'DATE', 'March', null);
+INSERT INTO text VALUES ('MONTH_03', 'eu', 'DATE', 'martxoa', null);
+INSERT INTO text VALUES ('MONTH_04', 'es', 'DATE', 'abril', null);
+INSERT INTO text VALUES ('MONTH_04', 'en', 'DATE', 'April', null);
+INSERT INTO text VALUES ('MONTH_04', 'eu', 'DATE', 'apirila', null);
+INSERT INTO text VALUES ('MONTH_05', 'es', 'DATE', 'mayo', null);
+INSERT INTO text VALUES ('MONTH_05', 'en', 'DATE', 'May', null);
+INSERT INTO text VALUES ('MONTH_05', 'eu', 'DATE', 'maiatza', null);
+INSERT INTO text VALUES ('MONTH_06', 'es', 'DATE', 'junio', null);
+INSERT INTO text VALUES ('MONTH_06', 'en', 'DATE', 'June', null);
+INSERT INTO text VALUES ('MONTH_06', 'eu', 'DATE', 'ekaina', null);
+INSERT INTO text VALUES ('MONTH_07', 'es', 'DATE', 'julio', null);
+INSERT INTO text VALUES ('MONTH_07', 'en', 'DATE', 'July', null);
+INSERT INTO text VALUES ('MONTH_07', 'eu', 'DATE', 'uztaila', null);
+INSERT INTO text VALUES ('MONTH_08', 'es', 'DATE', 'agosto', null);
+INSERT INTO text VALUES ('MONTH_08', 'en', 'DATE', 'August', null);
+INSERT INTO text VALUES ('MONTH_08', 'eu', 'DATE', 'abuztua', null);
+INSERT INTO text VALUES ('MONTH_09', 'es', 'DATE', 'septiembre', null);
+INSERT INTO text VALUES ('MONTH_09', 'en', 'DATE', 'September', null);
+INSERT INTO text VALUES ('MONTH_09', 'eu', 'DATE', 'iraila', null);
+INSERT INTO text VALUES ('MONTH_10', 'es', 'DATE', 'octubre', null);
+INSERT INTO text VALUES ('MONTH_10', 'en', 'DATE', 'October', null);
+INSERT INTO text VALUES ('MONTH_10', 'eu', 'DATE', 'urria', null);
+INSERT INTO text VALUES ('MONTH_11', 'es', 'DATE', 'noviembre', null);
+INSERT INTO text VALUES ('MONTH_11', 'en', 'DATE', 'November', null);
+INSERT INTO text VALUES ('MONTH_11', 'eu', 'DATE', 'azaroa', null);
+INSERT INTO text VALUES ('MONTH_12', 'es', 'DATE', 'diciembre', null);
+INSERT INTO text VALUES ('MONTH_12', 'en', 'DATE', 'December', null);
+INSERT INTO text VALUES ('MONTH_12', 'eu', 'DATE', 'abendua', null);
+INSERT INTO text VALUES ('DAY_1', 'es', 'DATE', 'lunes', null);
+INSERT INTO text VALUES ('DAY_1', 'en', 'DATE', 'Monday', null);
+INSERT INTO text VALUES ('DAY_1', 'eu', 'DATE', 'astelehena', null);
+INSERT INTO text VALUES ('DAY_2', 'es', 'DATE', 'martes', null);
+INSERT INTO text VALUES ('DAY_2', 'en', 'DATE', 'Tuesday', null);
+INSERT INTO text VALUES ('DAY_2', 'eu', 'DATE', 'asteartea', null);
+INSERT INTO text VALUES ('DAY_3', 'es', 'DATE', 'miércoles', null);
+INSERT INTO text VALUES ('DAY_3', 'en', 'DATE', 'Wednesday', null);
+INSERT INTO text VALUES ('DAY_3', 'eu', 'DATE', 'astezkena', null);
+INSERT INTO text VALUES ('DAY_4', 'es', 'DATE', 'jueves', null);
+INSERT INTO text VALUES ('DAY_4', 'en', 'DATE', 'Thursday', null);
+INSERT INTO text VALUES ('DAY_4', 'eu', 'DATE', 'osteguna', null);
+INSERT INTO text VALUES ('DAY_5', 'es', 'DATE', 'viernes', null);
+INSERT INTO text VALUES ('DAY_5', 'en', 'DATE', 'Friday', null);
+INSERT INTO text VALUES ('DAY_5', 'eu', 'DATE', 'ostirala', null);
+INSERT INTO text VALUES ('DAY_6', 'es', 'DATE', 'sábado', null);
+INSERT INTO text VALUES ('DAY_6', 'en', 'DATE', 'Saturday', null);
+INSERT INTO text VALUES ('DAY_6', 'eu', 'DATE', 'larunbata', null);
+INSERT INTO text VALUES ('DAY_7', 'es', 'DATE', 'domingo', null);
+INSERT INTO text VALUES ('DAY_7', 'en', 'DATE', 'Sunday', null);
+INSERT INTO text VALUES ('DAY_7', 'eu', 'DATE', 'igandea', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_YEAR', 'es', 'DATE', 'Desde #YEAR_START#', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_YEAR', 'en', 'DATE', 'Since #YEAR_START#', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_YEAR', 'eu', 'DATE', '#YEAR_START#tik', null);
+INSERT INTO text VALUES ('CV_DATE_DURING_YEAR', 'es', 'DATE', '#YEAR_START#', null);
+INSERT INTO text VALUES ('CV_DATE_DURING_YEAR', 'en', 'DATE', '#YEAR_START#', null);
+INSERT INTO text VALUES ('CV_DATE_DURING_YEAR', 'eu', 'DATE', '#YEAR_START#n', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_TO_YEAR', 'es', 'DATE', 'Desde #YEAR_START# hasta #YEAR_END#', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_TO_YEAR', 'en', 'DATE', 'From #YEAR_START# until #YEAR_END#', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_TO_YEAR', 'eu', 'DATE', '#YEAR_START#tik #YEAR_END#ra', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_MONTH', 'es', 'DATE', 'Desde #MONTH_START# de #YEAR_START#', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_MONTH', 'en', 'DATE', 'Since #MONTH_START# #YEAR_START#', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_MONTH', 'eu', 'DATE', '#YEAR_START#ko #MONTH_START#tik', null);
+INSERT INTO text VALUES ('CV_DATE_DURING_MONTH', 'es', 'DATE', '#MONTH_START# de #YEAR_START#', null);
+INSERT INTO text VALUES ('CV_DATE_DURING_MONTH', 'en', 'DATE', '#MONTH_START# #YEAR_START#', null);
+INSERT INTO text VALUES ('CV_DATE_DURING_MONTH', 'eu', 'DATE', '#YEAR_START#ko #MONTH_START#n', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_TO_MONTH', 'es', 'DATE', 'Desde #MONTH_START# de #YEAR_START# hasta #MONTH_END# de #YEAR_END#', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_TO_MONTH', 'en', 'DATE', 'From #MONTH_START# #YEAR_START# until #MONTH_END# #YEAR_END#', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_TO_MONTH', 'eu', 'DATE', '#YEAR_START#ko #MONTH_START#tik #YEAR_END#ko #MONTH_END#ra', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_TO_MONTH_YEAR', 'es', 'DATE', 'Desde #MONTH_START# hasta #MONTH_END# de #YEAR_START#', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_TO_MONTH_YEAR', 'en', 'DATE', 'From #MONTH_START# until #MONTH_END# #YEAR_START#', null);
+INSERT INTO text VALUES ('CV_DATE_FROM_TO_MONTH_YEAR', 'eu', 'DATE', '#YEAR_START#ko #MONTH_START#tik #MONTH_START#ra', null);
+
+-- SOCIAL
+INSERT INTO text VALUES ('SOCIAL_GITHUB', 'es', 'SOCIAL', 'Github', null);
+INSERT INTO text VALUES ('SOCIAL_GITHUB', 'en', 'SOCIAL', 'Github', null);
+INSERT INTO text VALUES ('SOCIAL_GITHUB', 'eu', 'SOCIAL', 'Github', null);
+INSERT INTO text VALUES ('SOCIAL_RSS', 'es', 'SOCIAL', 'RSS', null);
+INSERT INTO text VALUES ('SOCIAL_RSS', 'en', 'SOCIAL', 'RSS', null);
+INSERT INTO text VALUES ('SOCIAL_RSS', 'eu', 'SOCIAL', 'RSS', null);
+INSERT INTO text VALUES ('SOCIAL_MAIL', 'es', 'SOCIAL', 'Correo electrónico', null);
+INSERT INTO text VALUES ('SOCIAL_MAIL', 'en', 'SOCIAL', 'eMail', null);
+INSERT INTO text VALUES ('SOCIAL_MAIL', 'eu', 'SOCIAL', 'ePosta', null);
+INSERT INTO text VALUES ('SOCIAL_LINKEDIN', 'es', 'SOCIAL', 'LinkedIn', null);
+INSERT INTO text VALUES ('SOCIAL_LINKEDIN', 'en', 'SOCIAL', 'LinkedIn', null);
+INSERT INTO text VALUES ('SOCIAL_LINKEDIN', 'eu', 'SOCIAL', 'LinkedIn', null);
+
+
+-- TAGS
+INSERT INTO text VALUES ('TAG_JS',      'es', 'TAG', 'Javascript', null);
+INSERT INTO text VALUES ('TAG_JS',      'en', 'TAG', 'Javascript', null);
+INSERT INTO text VALUES ('TAG_JS',      'eu', 'TAG', 'Javascript', null);
+INSERT INTO text VALUES ('TAG_HTML5',   'es', 'TAG', 'HTML5', null);
+INSERT INTO text VALUES ('TAG_HTML5',   'en', 'TAG', 'HTML5', null);
+INSERT INTO text VALUES ('TAG_HTML5',   'eu', 'TAG', 'HTML5', null);
+INSERT INTO text VALUES ('TAG_ANDROID', 'es', 'TAG', 'Android', null);
+INSERT INTO text VALUES ('TAG_ANDROID', 'en', 'TAG', 'Android', null);
+INSERT INTO text VALUES ('TAG_ANDROID', 'eu', 'TAG', 'Android', null);
+INSERT INTO text VALUES ('TAG_IOS',     'es', 'TAG', 'iOS', null);
+INSERT INTO text VALUES ('TAG_IOS',     'en', 'TAG', 'iOS', null);
+INSERT INTO text VALUES ('TAG_IOS',     'eu', 'TAG', 'iOS', null);
+INSERT INTO text VALUES ('TAG_JAVA',    'es', 'TAG', 'Java', null);
+INSERT INTO text VALUES ('TAG_JAVA',    'en', 'TAG', 'Java', null);
+INSERT INTO text VALUES ('TAG_JAVA',    'eu', 'TAG', 'Java', null);
+INSERT INTO text VALUES ('TAG_SWIFT',   'es', 'TAG', 'Swift', null);
+INSERT INTO text VALUES ('TAG_SWIFT',   'en', 'TAG', 'Swift', null);
+INSERT INTO text VALUES ('TAG_SWIFT',   'eu', 'TAG', 'Swift', null);
+INSERT INTO text VALUES ('TAG_WEB',     'es', 'TAG', 'Web', null);
+INSERT INTO text VALUES ('TAG_WEB',     'en', 'TAG', 'Web', null);
+INSERT INTO text VALUES ('TAG_WEB',     'eu', 'TAG', 'Web', null);
+
+
+-- PAGE Header
+INSERT INTO text VALUES ('HEADER_HOME',     'es', 'HEADER', 'Inicio', null);
+INSERT INTO text VALUES ('HEADER_PROFILE',  'es', 'HEADER', 'Yo', null);
+INSERT INTO text VALUES ('HEADER_PROJECTS', 'es', 'HEADER', 'Proyectos', null);
+INSERT INTO text VALUES ('HEADER_BLOG',     'es', 'HEADER', 'Blog', null);
+INSERT INTO text VALUES ('HEADER_HOME',     'en', 'HEADER', 'Home', null);
+INSERT INTO text VALUES ('HEADER_PROFILE',  'en', 'HEADER', 'Me', null);
+INSERT INTO text VALUES ('HEADER_PROJECTS', 'en', 'HEADER', 'Projects', null);
+INSERT INTO text VALUES ('HEADER_BLOG',     'en', 'HEADER', 'Blog', null);
+INSERT INTO text VALUES ('HEADER_HOME',     'eu', 'HEADER', 'Hasiera', null);
+INSERT INTO text VALUES ('HEADER_PROFILE',  'eu', 'HEADER', 'Ni', null);
+INSERT INTO text VALUES ('HEADER_PROJECTS', 'eu', 'HEADER', 'Proiektuak', null);
+INSERT INTO text VALUES ('HEADER_BLOG',     'eu', 'HEADER', 'Blog', null);
+
+
+-- PAGE footer
+INSERT INTO text VALUES ('FOOTER_FOLLOW', 'es', 'FOOTER', 'Sígueme', null);
+INSERT INTO text VALUES ('FOOTER_COPY',   'es', 'FOOTER', 'Copyright Iñigo Valentin 2014 - 2017', null);
+INSERT INTO text VALUES ('FOOTER_HELP',   'es', 'FOOTER', 'Ayuda', null);
+INSERT INTO text VALUES ('FOOTER_FOLLOW', 'en', 'FOOTER', 'Follow me', null);
+INSERT INTO text VALUES ('FOOTER_COPY',   'en', 'FOOTER', 'Copyright Iñigo Valentin 2014 - 2017', null);
+INSERT INTO text VALUES ('FOOTER_HELP',   'en', 'FOOTER', 'Help', null);
+INSERT INTO text VALUES ('FOOTER_FOLLOW', 'eu', 'FOOTER', 'Jarraitu', null);
+INSERT INTO text VALUES ('FOOTER_COPY',   'eu', 'FOOTER', 'Copyright Iñigo Valentin 2014 - 2017', null);
+INSERT INTO text VALUES ('FOOTER_HELP',   'eu', 'FOOTER', 'Laguntza', null);
+
+
+-- PAGE project
+INSERT INTO text VALUES ('PROJECT_TITLE',           'es', 'PROJECT', 'Proyectos', null);
+INSERT INTO text VALUES ('PROJECT_TITLE',           'en', 'PROJECT', 'Projects', null);
+INSERT INTO text VALUES ('PROJECT_TITLE',           'eu', 'PROJECT', 'Projektuak', null);
+INSERT INTO text VALUES ('PROJECT_DESCRIPTION',     'es', 'PROJECT', '', null); --TODO
+INSERT INTO text VALUES ('PROJECT_DESCRIPTION',     'en', 'PROJECT', '', null); --TODO
+INSERT INTO text VALUES ('PROJECT_DESCRIPTION',     'eu', 'PROJECT', '', null); --TODO
+INSERT INTO text VALUES ('PROJECT_SEARCH',          'es', 'PROJECT', 'Buscar', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH',          'en', 'PROJECT', 'Search', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH',          'eu', 'PROJECT', 'Bilatu', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH_ADVANCED', 'es', 'PROJECT', 'Búsqueda avanzada', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH_ADVANCED', 'en', 'PROJECT', 'Advanced search', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH_ADVANCED', 'eu', 'PROJECT', 'Búsqueda avanzada', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH_TYPE',     'es', 'PROJECT', 'Tipo', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH_TYPE',     'en', 'PROJECT', 'Project type', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH_TYPE',     'eu', 'PROJECT', 'Tipu', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH_LICENSE',  'es', 'PROJECT', 'Licencia', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH_LICENSE',  'en', 'PROJECT', 'License', null);
+INSERT INTO text VALUES ('PROJECT_SEARCH_LICENSE',  'eu', 'PROJECT', 'Lizenzia', null);
+INSERT INTO text VALUES ('PROJECT_VERSION',         'es', 'PROJECT', 'Versión', null);
+INSERT INTO text VALUES ('PROJECT_VERSION',         'en', 'PROJECT', 'Version', null);
+INSERT INTO text VALUES ('PROJECT_VERSION',         'eu', 'PROJECT', 'Versión', null);
+INSERT INTO text VALUES ('PROJECT_CLOSE',           'es', 'PROJECT', 'Cerrar', null);
+INSERT INTO text VALUES ('PROJECT_CLOSE',           'en', 'PROJECT', 'Close', null);
+INSERT INTO text VALUES ('PROJECT_CLOSE',           'eu', 'PROJECT', 'Itxali', null);
+INSERT INTO text VALUES ('PROJECT_LICENSE_SUMMARY', 'es', 'PROJECT', 'En resumen:', null);
+INSERT INTO text VALUES ('PROJECT_LICENSE_SUMMARY', 'en', 'PROJECT', 'In a few words:', null);
+INSERT INTO text VALUES ('PROJECT_LICENSE_SUMMARY', 'eu', 'PROJECT', 'En resumen:', null);
+INSERT INTO text VALUES ('PROJECT_LICENSE_LEGAL',   'es', 'PROJECT', 'Texto completo:', null);
+INSERT INTO text VALUES ('PROJECT_LICENSE_LEGAL',   'en', 'PROJECT', 'Full legal text:', null);
+INSERT INTO text VALUES ('PROJECT_LICENSE_LEGAL',   'eu', 'PROJECT', 'Textu osoa:', null);
+
+
+-- PAGE blog
+INSERT INTO text VALUES ('BLOG_TITLE',       'es', 'BLOG', 'Blog', null);
+INSERT INTO text VALUES ('BLOG_TITLE',       'en', 'BLOG', 'Blog', null);
+INSERT INTO text VALUES ('BLOG_TITLE',       'eu', 'BLOG', 'Blog', null);
+INSERT INTO text VALUES ('BLOG_DESCRIPTION', 'es', 'BLOG', '', null); --TODO
+INSERT INTO text VALUES ('BLOG_DESCRIPTION', 'en', 'BLOG', '', null); --TODO
+INSERT INTO text VALUES ('BLOG_DESCRIPTION', 'eu', 'BLOG', '', null); --TODO
+INSERT INTO text VALUES ('BLOG_MORE',        'es', 'BLOG', 'leer más', null);
+INSERT INTO text VALUES ('BLOG_MORE',        'en', 'BLOG', 'read more', null);
+INSERT INTO text VALUES ('BLOG_MORE',        'eu', 'BLOG', 'gehiago irakurri', null);
+INSERT INTO text VALUES ('BLOG_TAGS',        'es', 'BLOG', 'Tags', null);
+INSERT INTO text VALUES ('BLOG_TAGS',        'en', 'BLOG', 'Tags', null);
+INSERT INTO text VALUES ('BLOG_TAGS',        'eu', 'BLOG', 'Tags', null);
+INSERT INTO text VALUES ('BLOG_SEARCH',      'es', 'BLOG', 'Buscar', null);
+INSERT INTO text VALUES ('BLOG_SEARCH',      'en', 'BLOG', 'Search', null);
+INSERT INTO text VALUES ('BLOG_SEARCH',      'eu', 'BLOG', 'Bilatu', null);
+INSERT INTO text VALUES ('BLOG_TAG_CLOUD',   'es', 'BLOG', 'Nube de tags', null);
+INSERT INTO text VALUES ('BLOG_TAG_CLOUD',   'en', 'BLOG', 'Tag Cloud', null);
+INSERT INTO text VALUES ('BLOG_TAG_CLOUD',   'eu', 'BLOG', 'Tag Cloud', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS',    'es', 'BLOG', 'Comentarios', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS',    'en', 'BLOG', 'Comments', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS',    'eu', 'BLOG', 'Comentarios', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS_0',  'es', 'BLOG', 'No hay comentarios aún.', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS_0',  'en', 'BLOG', 'No comments yet.', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS_0',  'eu', 'BLOG', 'No hay comentarios aún.', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS_1',  'es', 'BLOG', 'comentario.', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS_1',  'en', 'BLOG', 'comment.', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS_1',  'eu', 'BLOG', 'comantario.', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS_X',  'es', 'BLOG', 'comentarios.', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS_X',  'en', 'BLOG', 'comments.', null);
+INSERT INTO text VALUES ('BLOG_COMMENTS_X',  'eu', 'BLOG', 'comentarios.', null);
+
+
+-- PAGE profile
+INSERT INTO text VALUES ('PROFILE_TITLE',       'es', 'PROFILE', 'Yo', null);
+INSERT INTO text VALUES ('PROFILE_TITLE',       'en', 'PROFILE', 'Me', null);
+INSERT INTO text VALUES ('PROFILE_TITLE',       'eu', 'PROFILE', 'Ni', null);
+INSERT INTO text VALUES ('PROFILE_DESCRIPTION', 'es', 'PROFILE', 'Sobre mí', null); --TODO
+INSERT INTO text VALUES ('PROFILE_DESCRIPTION', 'en', 'PROFILE', 'About me', null); --TODO
+INSERT INTO text VALUES ('PROFILE_DESCRIPTION', 'eu', 'PROFILE', 'Niri buruz', null); --TODO
+
+
+-- TABLE project_type
+INSERT INTO text VALUES ('PROJECT_TYPE_D',         'es', 'DB_PROJECT_TYPE', 'Desarrollo', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_D',         'en', 'DB_PROJECT_TYPE', 'Development', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_D',         'eu', 'DB_PROJECT_TYPE', 'Development', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_D_SUMMARY', 'es', 'DB_PROJECT_TYPE', 'Desarrollo', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_D_SUMMARY', 'en', 'DB_PROJECT_TYPE', 'Development', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_D_SUMMARY', 'eu', 'DB_PROJECT_TYPE', 'Development', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_S',         'es', 'DB_PROJECT_TYPE', 'Sistemas', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_S',         'en', 'DB_PROJECT_TYPE', 'System', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_S',         'eu', 'DB_PROJECT_TYPE', 'System', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_S_SUMMARY', 'es', 'DB_PROJECT_TYPE', 'Sistemas', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_S_SUMMARY', 'en', 'DB_PROJECT_TYPE', 'System', null);
+INSERT INTO text VALUES ('PROJECT_TYPE_S_SUMMARY', 'eu', 'DB_PROJECT_TYPE', 'System', null);
+INSERT INTO project_type VALUES ('D', 'PROJECT_TYPE_D', 'PROJECT_TYPE_D_SUMMARY');
+INSERT INTO project_type VALUES ('S', 'PROJECT_TYPE_S', 'PROJECT_TYPE_S_SUMMARY');
+
+
+-- TABLE project_url_type
+INSERT INTO text VALUES ('URL_TYPE_W',         'es', 'DB_URL_TYPE', 'Ver sitio web', null);
+INSERT INTO text VALUES ('URL_TYPE_W',         'en', 'DB_URL_TYPE', 'Go to web site', null);
+INSERT INTO text VALUES ('URL_TYPE_W',         'eu', 'DB_URL_TYPE', 'Horrialdera', null);
+INSERT INTO text VALUES ('URL_TYPE_W_SUMMARY', 'es', 'DB_URL_TYPE', 'Ver sitio wew de este proyecto en funcionamiento.', null);
+INSERT INTO text VALUES ('URL_TYPE_W_SUMMARY', 'en', 'DB_URL_TYPE', 'See production web site.', null);
+INSERT INTO text VALUES ('URL_TYPE_W_SUMMARY', 'eu', 'DB_URL_TYPE', 'Horrialdera.', null);
+INSERT INTO text VALUES ('URL_TYPE_P',         'es', 'DB_URL_TYPE', 'Google Play', null);
+INSERT INTO text VALUES ('URL_TYPE_P',         'en', 'DB_URL_TYPE', 'Google Play', null);
+INSERT INTO text VALUES ('URL_TYPE_P',         'eu', 'DB_URL_TYPE', 'Google Play', null);
+INSERT INTO text VALUES ('URL_TYPE_P_SUMMARY', 'es', 'DB_URL_TYPE', 'Ver app en Google Play.', null);
+INSERT INTO text VALUES ('URL_TYPE_P_SUMMARY', 'en', 'DB_URL_TYPE', 'See on Google Play.', null);
+INSERT INTO text VALUES ('URL_TYPE_P_SUMMARY', 'eu', 'DB_URL_TYPE', 'Google Play-n.', null);
+INSERT INTO text VALUES ('URL_TYPE_A',         'es', 'DB_URL_TYPE', 'App Store', null);
+INSERT INTO text VALUES ('URL_TYPE_A',         'en', 'DB_URL_TYPE', 'App Store', null);
+INSERT INTO text VALUES ('URL_TYPE_A',         'eu', 'DB_URL_TYPE', 'App Store', null);
+INSERT INTO text VALUES ('URL_TYPE_A_SUMMARY', 'es', 'DB_URL_TYPE', 'Ver app en App Store.', null);
+INSERT INTO text VALUES ('URL_TYPE_A_SUMMARY', 'en', 'DB_URL_TYPE', 'See on App Store.', null);
+INSERT INTO text VALUES ('URL_TYPE_A_SUMMARY', 'eu', 'DB_URL_TYPE', 'App Store-an.', null);
+INSERT INTO text VALUES ('URL_TYPE_G',         'es', 'DB_URL_TYPE', 'Github', null);
+INSERT INTO text VALUES ('URL_TYPE_G',         'en', 'DB_URL_TYPE', 'Github', null);
+INSERT INTO text VALUES ('URL_TYPE_G',         'eu', 'DB_URL_TYPE', 'Github', null);
+INSERT INTO text VALUES ('URL_TYPE_G_SUMMARY', 'es', 'DB_URL_TYPE', 'Ver código fuente en Github.', null);
+INSERT INTO text VALUES ('URL_TYPE_G_SUMMARY', 'en', 'DB_URL_TYPE', 'Checkout source code on Github.', null);
+INSERT INTO text VALUES ('URL_TYPE_G_SUMMARY', 'eu', 'DB_URL_TYPE', 'Kodea Github-n.', null);
+INSERT INTO text VALUES ('URL_TYPE_M',         'es', 'DB_URL_TYPE', 'Ver', null);
+INSERT INTO text VALUES ('URL_TYPE_M',         'en', 'DB_URL_TYPE', 'See', null);
+INSERT INTO text VALUES ('URL_TYPE_M',         'eu', 'DB_URL_TYPE', 'Ikusi', null);
+INSERT INTO text VALUES ('URL_TYPE_M_SUMMARY', 'es', 'DB_URL_TYPE', 'Ver', null);
+INSERT INTO text VALUES ('URL_TYPE_M_SUMMARY', 'en', 'DB_URL_TYPE', 'See', null);
+INSERT INTO text VALUES ('URL_TYPE_M_SUMMARY', 'eu', 'DB_URL_TYPE', 'Ikusi', null);
+INSERT INTO project_url_type VALUES ('E', null, null, null);
+INSERT INTO project_url_type VALUES ('W', 'URL_TYPE_W', 'URL_TYPE_W_SUMMARY', 'web.png');
+INSERT INTO project_url_type VALUES ('P', 'URL_TYPE_P', 'URL_TYPE_P_SUMMARY', 'googleplay.png');
+INSERT INTO project_url_type VALUES ('A', 'URL_TYPE_A', 'URL_TYPE_A_SUMMARY', 'appstore.png');
+INSERT INTO project_url_type VALUES ('G', 'URL_TYPE_G', 'URL_TYPE_G_SUMMARY', 'github.png');
+INSERT INTO project_url_type VALUES ('M', 'URL_TYPE_M', 'URL_TYPE_M_SUMMARY', 'misc.png');
+
+
+-- TABLE project_version_type
+INSERT INTO text VALUES ('VERSION_TYPE_ALPHA',         'es', 'VERSION_TYPE', 'Alfa', null);
+INSERT INTO text VALUES ('VERSION_TYPE_ALPHA',         'en', 'VERSION_TYPE', 'Alpha', null);
+INSERT INTO text VALUES ('VERSION_TYPE_ALPHA',         'eu', 'VERSION_TYPE', 'Alpha', null);
+INSERT INTO text VALUES ('VERSION_TYPE_ALPHA_SUMMARY', 'es', 'VERSION_TYPE', 'En sesarrollo. Inestable.', null);
+INSERT INTO text VALUES ('VERSION_TYPE_ALPHA_SUMMARY', 'en', 'VERSION_TYPE', 'In development. Unstable.', null);
+INSERT INTO text VALUES ('VERSION_TYPE_ALPHA_SUMMARY', 'eu', 'VERSION_TYPE', 'En desarrollo. Inestable', null);
+INSERT INTO text VALUES ('VERSION_TYPE_BETA',          'es', 'VERSION_TYPE', 'Beta', null);
+INSERT INTO text VALUES ('VERSION_TYPE_BETA',          'en', 'VERSION_TYPE', 'Beta', null);
+INSERT INTO text VALUES ('VERSION_TYPE_BETA',          'eu', 'VERSION_TYPE', 'Beta', null);
+INSERT INTO text VALUES ('VERSION_TYPE_BETA_SUMMARY',  'es', 'VERSION_TYPE', 'En pruebas. Parcialmente estable.', null);
+INSERT INTO text VALUES ('VERSION_TYPE_BETA_SUMMARY',  'en', 'VERSION_TYPE', 'Under testing. Stability not guaranteed.', null);
+INSERT INTO text VALUES ('VERSION_TYPE_BETA_SUMMARY',  'eu', 'VERSION_TYPE', 'En pruebas. Parcialmente estable.', null);
+INSERT INTO text VALUES ('VERSION_TYPE_PRO',           'es', 'VERSION_TYPE', 'Estable', null);
+INSERT INTO text VALUES ('VERSION_TYPE_PRO',           'en', 'VERSION_TYPE', 'Stable', null);
+INSERT INTO text VALUES ('VERSION_TYPE_PRO',           'eu', 'VERSION_TYPE', 'Estable', null);
+INSERT INTO text VALUES ('VERSION_TYPE_PRO_SUMMARY',   'es', 'VERSION_TYPE', 'Listo para uso en entorno de producción.', null);
+INSERT INTO text VALUES ('VERSION_TYPE_PRO_SUMMARY',   'en', 'VERSION_TYPE', 'Ready for production.', null);
+INSERT INTO text VALUES ('VERSION_TYPE_PRO_SUMMARY',   'eu', 'VERSION_TYPE', 'Listo para uso en entorno de producción.', null);
+INSERT INTO project_version_type VALUES ('A', 'VERSION_TYPE_ALPHA', 'VERSION_TYPE_ALPHA_SUMMARY', '#ff0000');
+INSERT INTO project_version_type VALUES ('B', 'VERSION_TYPE_BETA',  'VERSION_TYPE_BETA_SUMMARY',  '#ffaa00');
+INSERT INTO project_version_type VALUES ('P', 'VERSION_TYPE_PRO',   'VERSION_TYPE_PRO_SUMMARY',   '#00ff00');
+
+
+-- TABLE license
+INSERT INTO text VALUES ('LICENSE_CC-BY-SA_SUMMARY', 'es', 'LICENSE', null, 'LICENSE_CC-BY-SA_SUMMARY__ES');
+INSERT INTO text VALUES ('LICENSE_CC-BY-SA_SUMMARY', 'en', 'LICENSE', null, 'LICENSE_CC-BY-SA_SUMMARY__EN');
+INSERT INTO text VALUES ('LICENSE_CC-BY-SA_SUMMARY', 'eu', 'LICENSE', null, 'LICENSE_CC-BY-SA_SUMMARY__EU');
+INSERT INTO text VALUES ('LICENSE_CC-BY-SA',         'es', 'LICENSE', null, 'LICENSE_CC-BY-SA__ES');
+INSERT INTO text VALUES ('LICENSE_CC-BY-SA',         'en', 'LICENSE', null, 'LICENSE_CC-BY-SA__EN');
+INSERT INTO text VALUES ('LICENSE_CC-BY-SA',         'eu', 'LICENSE', null, 'LICENSE_CC-BY-SA__EU');
+INSERT INTO text VALUES ('LICENSE_GPLV3_SUMMARY',    'es', 'LICENSE', null, 'LICENSE_GPLV3_SUMMARY__ES');
+INSERT INTO text VALUES ('LICENSE_GPLV3_SUMMARY',    'en', 'LICENSE', null, 'LICENSE_GPLV3_SUMMARY__EN');
+INSERT INTO text VALUES ('LICENSE_GPLV3_SUMMARY',    'eu', 'LICENSE', null, 'LICENSE_GPLV3_SUMMARY__EU');
+INSERT INTO text VALUES ('LICENSE_GPLV3',            'es', 'LICENSE', null, 'LICENSE_GPLV3__ES');
+INSERT INTO text VALUES ('LICENSE_GPLV3',            'en', 'LICENSE', null, 'LICENSE_GPLV3__EN');
+INSERT INTO text VALUES ('LICENSE_GPLV3',            'eu', 'LICENSE', null, 'LICENSE_GPLV3__EU');
+INSERT INTO license VALUES ('CC-BY-SA', 'LICENSE_CC-BY-SA_SUMMARY', 'LICENSE_CC-BY-SA', 'CC-BY-SA.png', 'CC-BY-SA.png');
+INSERT INTO license VALUES ('GPLv3',    'LICENSE_GPLV3_SUMMARY',    'LICENSE_GPLV3',    'GPLV3.png',    'GPLV3.png');
+
+
+-- TABLE cv_category
+INSERT INTO text VALUES ('CV_CATEGORY_WORK',             'es', 'DB_CV', 'Experiencia', null);
+INSERT INTO text VALUES ('CV_CATEGORY_WORK',             'en', 'DB_CV', 'Experience', null);
+INSERT INTO text VALUES ('CV_CATEGORY_WORK',             'eu', 'DB_CV', 'Experiencia', null);
+INSERT INTO text VALUES ('CV_CATEGORY_EDUCATION',        'es', 'DB_CV', 'Formación', null);
+INSERT INTO text VALUES ('CV_CATEGORY_EDUCATION',        'en', 'DB_CV', 'Hezkuntza', null);
+INSERT INTO text VALUES ('CV_CATEGORY_EDUCATION',        'eu', 'DB_CV', 'Education', null);
+INSERT INTO text VALUES ('CV_CATEGORY_PROJECTS',         'es', 'DB_CV', 'Otros proyectos', null);
+INSERT INTO text VALUES ('CV_CATEGORY_PROJECTS',         'en', 'DB_CV', 'Other projects', null);
+INSERT INTO text VALUES ('CV_CATEGORY_PROJECTS',         'eu', 'DB_CV', 'Beste projecktuak', null);
+INSERT INTO text VALUES ('CV_CATEGORY_PROJECTS_SUMMARY', 'es', 'DB_CV', 'Proyectos de software eralizados por cuenta propia', null);
+INSERT INTO text VALUES ('CV_CATEGORY_PROJECTS_SUMMARY', 'en', 'DB_CV', 'Proyectos de software eralizados por cuenta propia', null);
+INSERT INTO text VALUES ('CV_CATEGORY_PROJECTS_SUMMARY', 'eu', 'DB_CV', 'Proyectos de software eralizados por cuenta propia', null);
+INSERT INTO text VALUES ('CV_CATEGORY_OTHER',            'es', 'DB_CV', 'Otra experiencia laboral', null);
+INSERT INTO text VALUES ('CV_CATEGORY_OTHER',            'en', 'DB_CV', 'Otra experiencia laboral', null);
+INSERT INTO text VALUES ('CV_CATEGORY_OTHER',            'eu', 'DB_CV', 'Otra experiencia laboral', null);
+INSERT INTO text VALUES ('CV_CATEGORY_OTHER_SUMMARY',    'es', 'DB_CV', 'Experiencia laboral fuera del ámbito de IT', null);
+INSERT INTO text VALUES ('CV_CATEGORY_OTHER_SUMMARY',    'en', 'DB_CV', 'Experiencia laboral fuera del ámbito de IT', null);
+INSERT INTO text VALUES ('CV_CATEGORY_OTHER_SUMMARY',    'eu', 'DB_CV', 'Experiencia laboral fuera del ámbito de IT', null);
+INSERT INTO cv_category VALUES (1, 'CV_CATEGORY_WORK',      null,                          1 , 1);
+INSERT INTO cv_category VALUES (2, 'CV_CATEGORY_EDUCATION', null,                          1 , 3);
+INSERT INTO cv_category VALUES (3, 'CV_CATEGORY_PROJECTS', 'CV_CATEGORY_PROJECTS_SUMMARY', 0 , 2);
+INSERT INTO cv_category VALUES (4, 'CV_CATEGORY_OTHER',    'CV_CATEGORY_OTHER_SUMMARY',    1 , 4);
+
+
+-- ADD MORE ROWS AS YOU SEE FIT:

+ 7 - 0
sql/README.md

@@ -0,0 +1,7 @@
+To create de database:
+
+1) Edit the file 01_STRUCTURE.SQL. Replace both passwords with two of your own. Uncomment all GRANT entries.
+
+2) Modify the file 02_DATA.SQL at will to fit your needs.
+
+3) Run the __SETUP.SQL script.

+ 8 - 0
sql/__SETUP.SQL

@@ -0,0 +1,8 @@
+-- DB structure.
+SOURCE 00_STRUCTURE.SQL;
+
+-- DB permissions. Entries are commented by default.
+SOURCE 01_PERMISSIONS.SQL;
+
+-- Initial data.
+SOURCE 02_DATA.SQL;

+ 0 - 17
sql/dbusers-example.sql

@@ -1,17 +0,0 @@
--- Replace all instances of  XXX with the external site db password
-GRANT SELECT ON iv.lang TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT ON iv.text TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT ON iv.user TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT ON iv.post TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT ON iv.post_tag TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT ON iv.post_image TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT, INSERT ON iv.post_comment TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT ON iv.project TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT ON iv.project_url TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT ON iv.project_image TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT ON iv.project_tag TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT, INSERT ON iv.project_comment TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-GRANT SELECT ON iv.settings TO 'ivext'@'localhost' IDENTIFIED BY 'XXX';
-
---Replace YYY with the internal site db password
-GRANT SELECT, INSERT, DELETE, UPDATE, SHOW VIEW ON iv.* TO 'ivint'@'localhost' IDENTIFIED BY 'YYY';

+ 0 - 30
sql/text.sql

@@ -1,30 +0,0 @@
--- AVAILABLE LANGUAGES
-INSERT INTO lang VALUES ('es', 'Castellano', '1');
-INSERT INTO lang VALUES ('en', 'English', '1');
-INSERT INTO lang VALUES ('eu', 'Euskara', '1');
-
--- HEADER
-INSERT INTO text VALUES ('HEADER_HOME',     'es', 'HEADER', 'Inicio');
-INSERT INTO text VALUES ('HEADER_ME',       'es', 'HEADER', 'Yo');
-INSERT INTO text VALUES ('HEADER_PROJECTS', 'es', 'HEADER', 'Proyectos');
-INSERT INTO text VALUES ('HEADER_BLOG',     'es', 'HEADER', 'Blog');
-INSERT INTO text VALUES ('HEADER_HOME',     'en', 'HEADER', 'Home');
-INSERT INTO text VALUES ('HEADER_ME',       'en', 'HEADER', 'Me');
-INSERT INTO text VALUES ('HEADER_PROJECTS', 'en', 'HEADER', 'Projects');
-INSERT INTO text VALUES ('HEADER_BLOG',     'en', 'HEADER', 'Blog');
-INSERT INTO text VALUES ('HEADER_HOME',     'eu', 'HEADER', 'Hasiera');
-INSERT INTO text VALUES ('HEADER_ME',       'eu', 'HEADER', 'Ni');
-INSERT INTO text VALUES ('HEADER_PROJECTS', 'eu', 'HEADER', 'Proiektuak');
-INSERT INTO text VALUES ('HEADER_BLOG',     'eu', 'HEADER', 'Blog');
-
-
--- FOOTER
-INSERT INTO text VALUES ('FOOTER_FOLLOW', 'es', 'FOOTER', 'Sígueme');
-INSERT INTO text VALUES ('FOOTER_COPY',   'es', 'FOOTER', 'Copyright Iñigo Valentin 2014 - 2017');
-INSERT INTO text VALUES ('FOOTER_HELP',   'es', 'FOOTER', 'Ayuda');
-INSERT INTO text VALUES ('FOOTER_FOLLOW', 'en', 'FOOTER', 'Follow me');
-INSERT INTO text VALUES ('FOOTER_COPY',   'en', 'FOOTER', 'Copyright Iñigo Valentin 2014 - 2017');
-INSERT INTO text VALUES ('FOOTER_HELP',   'en', 'FOOTER', 'Help');
-INSERT INTO text VALUES ('FOOTER_FOLLOW', 'eu', 'FOOTER', 'Jarraitu');
-INSERT INTO text VALUES ('FOOTER_COPY',   'eu', 'FOOTER', 'Copyright Iñigo Valentin 2014 - 2017');
-INSERT INTO text VALUES ('FOOTER_HELP',   'eu', 'FOOTER', 'Laguntza');

+ 24 - 0
utils/imagescaler

@@ -0,0 +1,24 @@
+#!/bin/bash
+for folder in www/img/*; do
+	if [[ -d $folder ]]; then
+		for file in $folder/*; do
+			if [[ -f $file ]]; then
+				echo $file;
+				for i in {1..32}; do
+					if [ -d $folder/x$i ]; then
+						d=$((i*33))
+						convert "$file" -resize ${d}x${d}  $folder/x$i/${file:${#folder}+1:100}
+					fi
+				done
+			fi
+		done
+	fi
+done;
+
+#for file in *.jpg;do
+#    convert "$file" -resize 800x800  "view/$file"
+#    convert "$file" -resize 600x600  "preview/$file"
+#    convert "$file" -resize 340x340  "miniature/$file"
+#    convert "$file" -resize 180x180  "thumb/$file"
+#done
+

+ 68 - 0
www-admin/authentication.php

@@ -0,0 +1,68 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (check_session($con)){
+        header("Location: /");
+        exit(-1);
+    }
+    else{
+?>
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta content="text/html; charset=windows-1252" http-equiv="content-type"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
+        <link rel='shortcut icon' href='<?=$pserver?>/img/logo/x2/favicon.ico'/>
+        <title>Administration</title>
+        <style>
+<?php
+            include $doc_root . "css/ui.css";
+            include $doc_root . "css/home.css";
+?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media='(max-width : 990px)'>
+<?php
+            include $doc_root . "css/m/ui.css";
+            include $doc_root . "css/m/home.css";
+?>
+        </style>
+        <!-- Script files -->
+        <script type='text/javascript'>
+<?php
+            include $doc_root . "script/ui.js";
+?>
+        </script>
+    </head>
+    <body>
+        <div class='section' id='authentication'>
+            <h3 class='section_title'>Log In</h3>
+            <div class='entry'>
+                <form method='post' action='/login.php'>
+                    <table>
+                        <tr>
+                            <td>User:</td>
+                            <td><input name="user" type="text"/></td>
+                        </tr>
+                        <tr>
+                            <td>Password:</td>
+                            <td><input name="pass" type="password"/></td>
+                        </tr>
+                        <tr>
+                            <td></td><td><input type="submit" value="Entrar"/></td>
+                        </tr>
+                    </table>
+                </form>
+            </div>
+        </div>
+    </body>
+</html>
+<?php
+    }
+?>

+ 31 - 0
www-admin/blog/delete.php

@@ -0,0 +1,31 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401); // Not logged in: Forbidden.
+    }
+    else{
+        $id = intval(mysqli_real_escape_string($con, $_GET["p"]));
+        if (strlen($id) < 1){
+            http_response_code(400); // No or bad ID: Bad request.
+        }
+        else{
+            if (mysqli_num_rows(mysqli_query($con, "SELECT id FROM post WHERE id = $id;")) == 0){
+                http_response_code(404); // Unexistent id: Not found.
+            }
+            else{
+                mysqli_query($con, "DELETE FROM post_tag WHERE project = $id;");
+                mysqli_query($con, "DELETE FROM post_image WHERE project = $id;");
+                mysqli_query($con, "DELETE FROM post_comment WHERE project = $id;");
+                mysqli_query($con, "DELETE FROM post WHERE id = $id;");
+                http_response_code(200); //OK.
+            }
+        }
+    }
+?>

+ 28 - 0
www-admin/blog/deletecomment.php

@@ -0,0 +1,28 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401); // Not logged in: Forbidden.
+    }
+    else{
+        $id = intval(mysqli_real_escape_string($con, $_GET["i"]));
+        if (strlen($id) < 1){
+            http_response_code(400); // No or bad ID: Bad request.
+        }
+        else{
+            if (mysqli_num_rows(mysqli_query($con, "SELECT id FROM post_comment WHERE id = $id;")) == 0){
+                http_response_code(404); // Unexistent id: Not found.
+            }
+            else{
+                mysqli_query($con, "DELETE FROM post_comment WHERE id = $id;");
+                http_response_code(200); //OK.
+            }
+        }
+    }
+?>

+ 295 - 0
www-admin/blog/edit.php

@@ -0,0 +1,295 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $lang = "en";
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = $proto . substr($http_host, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401);
+        header("Location: /authentication.php");
+    }
+    else{
+        $id = intval(mysqli_real_escape_string($con, $_GET["p"]));
+        if (strlen($id) < 1){
+            http_response_code(400); // No or bad ID: Bad request.
+            header("Location: /blog/");
+        }
+        else{
+            $q_post = mysqli_query($con, "SELECT * FROM post WHERE id = $id;");
+            if (mysqli_num_rows($q_post) == 0){
+                http_response_code(404); // Unexistent id: Not found.
+                header("Location: /blog/");
+            }
+            else{
+                $r_post = mysqli_fetch_array($q_post);
+                $id = $r_post["id"];
+                $title = text($con, $r_post["title"], $lang);
+                $q_lang = mysqli_query($con, "SELECT * FROM lang;");
+?>
+<html>
+    <head>
+        <meta content='text/html; charset=windows-1252' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'>
+        <link rel='shortcut icon' href='<?=$pserver?>/img/logo/x2/favicon.ico'/>
+        <title><?=$title?>I&ntilde;igo Valentin - Administration Panel</title>
+        <style>
+<?php
+            include $doc_root . "css/ui.css";
+            include $doc_root . "css/blog.css";
+?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media='(max-width : 990px)'>
+<?php
+            include $doc_root . "css/m/ui.css";
+            include $doc_root . "css/m/blog.css";
+?>
+        </style>
+        <!-- Script files -->
+        <script type='text/javascript'>
+<?php
+            include $doc_root . "script/ui.js";
+            include $doc_root . "script/blog.js";
+?>
+        </script>
+         <script src='/script/ckeditor/ckeditor.js'></script>
+    </head>
+    <body>
+<?php
+        include $doc_root . "header.php";
+?>
+        <div id='content'>
+            <div class='section' id='post_edit'>
+                <h3 class='section_title'><?=$title?></h3>
+                <div class='entry'>
+                    <div id='lang_tabs'>
+                        <table>
+                            <tr>
+<?php
+                                mysqli_data_seek($q_lang, 0);
+                                $active = 'lang_tabs_active';
+                                while ($r_lang = mysqli_fetch_array($q_lang)){
+?>
+                                    <td class='pointer <?=$active?>' id='lang_tab_<?=$r_lang["code"]?>' onclick='showLanguage("<?=$r_lang["code"]?>");'>
+                                        <?=$r_lang["name"]?>
+                                    </td>
+<?php
+                                    $active = '';
+                                }
+?>
+                            </tr>
+                        </table>
+                    </div> <!-- #lang_tabs -->
+                    <span class='bold'>URL:</span>
+                    <?=$pserver?>/blog/
+                    <span class='editable'>
+                        <span class='field_result'><?=$r_post["permalink"]?></span>
+                        <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                        <input class='field_editable' type='text' value='<?=$r_post["permalink"]?>'/>
+                        <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "post", "permalink", <?=$id?>);'/>
+                        <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                    </span>
+                    <hr class='separator'/>
+<?php
+                    mysqli_data_seek($q_lang, 0);
+                    $active = "content_lang_active";
+                    while ($r_lang = mysqli_fetch_array($q_lang)){
+?>
+                        <div class='content_lang_<?=$r_lang["code"]?> content_lang <?=$active?>'>
+                            <div class='editable'>
+                                <span class='bold'>Title (<?=$r_lang["name"]?>):</span>
+                                <span class='field_result'><?=text($con, $r_post["title"], $r_lang["code"])?></span>
+                                <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                <input class='field_editable' type='text' value='<?=text($con, $r_post["title"], $r_lang["code"])?>'/>
+                                <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "text", "<?=$r_lang["code"]?>", "<?=$r_post["title"]?>");'/>
+                                <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                            </div>
+                            <hr class='separator'/>
+                            <div class='editable'>
+                                <span class='bold'>Text (<?=$r_lang["name"]?>):</span>
+                                <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "text", "<?=$r_lang["code"]?>", "<?=$r_post["text"]?>");'/>
+                                <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                                <br/>
+                                <p class='field_result'><?=text($con, $r_post["text"], $r_lang["code"])?></p>
+                                <textarea id='text_<?=$r_lang["code"]?>' class='field_editable'><?=text($con, $r_post["text"], $r_lang["code"])?></textarea>
+                                <script>CKEDITOR.replace('text_<?=$r_lang["code"]?>');</script>
+                            </div>
+                        </div> <!-- .content_lang_<?=$r_lang["code"]?> -->
+<?php
+                        $active = '';
+                    }
+?>
+                    <hr class='separator'/>
+                    <h4>Images</h4>
+                    <div id='image_reel' class='horizontal_scroll'>
+<?php
+                        $q_image = mysqli_query($con, "SELECT id, image FROM post_image WHERE post = $id;");
+                        while ($r_image = mysqli_fetch_array($q_image)){
+?>
+                            <div class='post_image' id='post_image_<?=$r_image["id"]?>'>
+                                <img alt='<?=$title?>' title='<?=$title?>' src='<?=$pserver?>/img/blog/x4/<?=$r_image["image"]?>' />
+                                <br/>
+                                <label class="image_control image_upload">
+                                    <input id="image" onchange="preview_image();" type="file" name="image" accept="image/x-png, image/gif, image/jpeg"/>
+                                    @
+                                </label>
+                                <input class='image_control' type='button' onClick='deleteImage(<?=$r_image["id"]?>)' value='X' />
+                            </div>
+<?php
+                        }
+?>
+                        <div class='post_image' id='post_image_add'>
+                            <img onClick='addImage(<?=$id?>)' alt='Add image' title='Add image' src='<?=$server?>/img/icon/addimage.png' />
+                            <input type='file' id='header_img_selector' onChange='uploadHeaderImage(event, <?=$r["id"]?>);'/>
+                            <img id="image_preview" src="<?php echo $img; ?>"/><br/><br/>
+                        </div>
+                    </div>
+                    <hr class='separator'/>
+                    <h4>Tags</h4>
+                    TODO
+                    <hr class='separator'/>
+                    <h4>Settings</h4>
+<?php
+                    if ($r_post["visible"] == 1){
+                        $v = "Yes";
+                        $selected1 = "selected";
+                        $selected0 = "";
+                    }
+                    else{
+                        $v = "No";
+                        $selected1 = "";
+                        $selected0 = "selected";
+                    }
+?>
+                    <div class='editable'>
+                        <span class='bold'>Visible:</span>
+                        <span class='field_result'><?=$v?></span>
+                        <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                        <select class='field_editable'>
+                            <option value='1' <?=$selected1?>>Yes</option>
+                            <option value='0' <?=$selected0?>>No</option>
+                        </select>
+                        <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "post", "visible", "<?=$id?>");'/>
+                        <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                    </div>
+<?php
+                    if ($r_post["comments"] == 1){
+                        $v = "Yes";
+                        $selected1 = "selected";
+                        $selected0 = "";
+                    }
+                    else{
+                        $v = "No";
+                        $selected1 = "";
+                        $selected0 = "selected";
+                    }
+?>
+                    <div class='editable'>
+                        <span class='bold'>Comments enabled:</span>
+                        <span class='field_result'><?=$v?></span>
+                        <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                        <select class='field_editable'>
+                            <option value='1' <?=$selected1?>>Yes</option>
+                            <option value='0' <?=$selected0?>>No</option>
+                        </select>
+                        <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "post", "comments", "<?=$id?>");'/>
+                        <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                    </div>
+                    <input type='button' onclick='deletePost(<?=$id?>, "<?=$title?>");' value='Delete post'/>
+                    <h4>Comments</h4>
+<?php
+                        $q_comment = mysqli_query($con, "SELECT post_comment.id AS id, text, dtime, user, username, lang, (SELECT name FROM lang WHERE code = post_comment.lang) AS lang_name, approved, ip, browser, os, uagent FROM post_comment, stat_visit WHERE stat_visit.id = visit AND post = $id;");
+                        if (mysqli_num_rows($q_comment) == 0){
+?>
+                            <span>No comments yet</span>
+<?php
+                        }
+                        else{
+?>
+                            <table id='comments'>
+<?php
+                            while ($r_comment = mysqli_fetch_array($q_comment)){
+?>
+                                <tr>
+                                    <td class='text'>
+                                        <p><?=$r_comment["text"]?></p>
+                                    </td>
+                                    <td class='details'>
+                                        On <?=format_date($r_comment["dtime"], $lang, true)?>
+                                        <br/>
+<?php
+                                        if (strlen($r_comment["user"]) > 0){
+                                            $q_user = mysqli_query($con, "SELECT concat(fname, concat(' ', lname)) AS name, username FROM user WHERE id = $r_comment[user];");
+                                            $r_user = mysqli_fetch_array($q_user);
+?>
+                                            By <?=$r_user["name"]?> (<span class='bold'><?=$r_user["username"]?></span>)
+<?php
+                                        }
+                                        else{
+?>
+                                            By <?=$r_comment["username"]?>
+<?php
+                                        }
+                                        if (strlen($r_comment["lang_name"]) > 0){
+                                            $strlang = $r_comment["lang_name"];
+                                        }
+                                        else{
+                                            $strlang = $r_comment["lang"];
+                                        }
+                                        if (file_exists($doc_root . "img/lang/" . $r_comment["lang"] . ".gif")){
+                                            $imgpath = $doc_root . "img/lang/" . $r_comment["lang"] . ".gif";
+                                            $strlang = $strlang . " <img class='lang' title='$r_comment[lang]' alt='$r_comment[lang]' src='$server/$imgpath' />";
+                                        }
+?>
+                                        <br/>
+                                        Language: <?=$strlang?>
+<?php
+                                        if ($r_post["visible"] == 1){
+                                            $v = "Yes";
+                                            $selected1 = "selected";
+                                            $selected0 = "";
+                                        }
+                                        else{
+                                            $v = "No";
+                                            $selected1 = "";
+                                            $selected0 = "selected";
+                                        }
+?>
+                                        <div class='editable'>
+                                            Approved:
+                                            <span class='field_result'><?=$v?></span>
+                                            <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                            <select class='field_editable'>
+                                                <option value='1' <?=$selected1?>>Yes</option>
+                                                <option value='0' <?=$selected0?>>No</option>
+                                            </select>
+                                            <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "post_comment", "approved", "<?=$r_comment["id"]?>");'/>
+                                            <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                                        </div>
+                                    </td>
+                                    <td class='actions'>
+                                        <input type='button' onclick='deletePostComment(<?=$id?>);' value='Delete'/>
+                                    </td>
+                                </tr>
+<?php
+                            }
+?>
+                            </table>
+<?php
+                        }
+?>
+                </div> <!-- .entry -->
+            </div> <!-- .section -->
+        </div> <!-- #content -->
+    </body>
+</html>
+<?php
+            }
+        }
+    }
+?>

+ 149 - 0
www-admin/blog/index.php

@@ -0,0 +1,149 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    $lang = "en";
+    if (!check_session($con)){
+        header("Location: /authentication.php");
+        exit(-1);
+    }
+    else{
+?>
+<html>
+    <head>
+        <meta content='text/html; charset=windows-1252' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'>
+        <link rel='shortcut icon' href='<?=$pserver?>/img/logo/x2/favicon.ico'/>
+        <title>Blog - I&ntilde;igo Valentin - Administration Panel</title>
+        <style>
+<?php
+            include $doc_root . "css/ui.css";
+            include $doc_root . "css/blog.css";
+?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media='(max-width : 990px)'>
+<?php
+            include $doc_root . "css/m/ui.css";
+            include $doc_root . "css/m/blog.css";
+?>
+        </style>
+        <!-- Script files -->
+        <script type='text/javascript'>
+<?php
+            include $doc_root . "script/ui.js";
+            include $doc_root . "script/blog.js";
+?>
+        </script>
+    </head>
+    <body>
+<?php
+        include $doc_root . "header.php";
+?>
+        <div id='content'>
+            <div class='section'>
+                <h3 class='section_title'>Post list</h3>
+                <div class='entry'>
+                    <table id='post_list'>
+                        <tr>
+                            <th>Post</th>
+                            <th>Detalles</th>
+                            <th>Acciones</th>
+                        </tr>
+<?php
+                        $q_post = mysqli_query($con, "SELECT post.id AS id, permalink, title, text, dtime, comments, (SELECT count(id) FROM post_comment WHERE post = post.id) AS comment_count, concat(fname, concat(' ', lname)) AS user, visible, comments, (SELECT image FROM post_image WHERE post = post.id ORDER BY idx LIMIT 1) AS image, (SELECT count(image) FROM post_image WHERE post = post.id) AS image_count FROM post, user WHERE user = user.id");
+                        while ($r_post = mysqli_fetch_array($q_post)){
+                            $title = text($con, $r_post["title"], $lang);
+?>
+                            <tr>
+                                <td class='td_post'>
+                                    <h4>
+                                        <a title='View <?=$title?> on a new tab' target='_blank' href='<?=$pserver?>/blog/<?=$r_post["permalink"]?>'>
+                                            <?=$title?>
+                                        </a>
+                                    </h4>
+                                    <p><?=cut_text(text($con, $r_post["text"], $lang), 200)?></p>
+<?php
+                                    if ($r_post["image_count"] > 0){
+?>
+                                        <img class='post_image' alt='<?=$title?>' title='<?=$title?>' src='<?=$pserver?>/img/blog/x2/<?=$r_post["image"]?>' />
+<?php
+                                        if ($r_post["image_count"] == 2){
+?>
+                                            + other image.
+<?php
+                                        }
+                                        elseif ($r_post["image_count"] > 2){
+                                            $pl = $r_post["image_count"] - 1;
+?>
+                                            + <?=$pl?> other images.
+<?php
+                                        }
+                                    }
+?>
+                                </td>
+                                <td class='td_details'>
+                                    By <?=$r_post["user"]?>.<br/>
+                                    Posted <?=format_date($r_post["dtime"], $lang, true)?>.<br/>
+                                    Visible:
+<?php
+                                    if ($r_post["visible"] == 1){
+?>
+                                        Yes
+<?php
+                                    }
+                                    else{
+?>
+                                        No
+<?php
+                                    }
+?>
+                                    <br/>Comments: <?=$r_post["comment_count"]?><br/>
+                                    Comments allowed:
+<?php
+                                    if ($r_post["comments"] == 1){
+?>
+                                        Yes
+<?php
+                                    }
+                                    else{
+?>
+                                        No
+<?php
+                                    }
+?>
+                                </td>
+                                <td class='td_actions'>
+                                    <input type='button' onclick='managePost(<?=$r_post["id"]?>);' value='Manage / Translate'/>
+                                    <input type='button' onclick='deletePost(<?=$r_post["id"]?>, "<?=$title?>");' value='Delete'/>
+                                    <?php
+                                    if ($r_post["visible"] == 1){
+?>
+                                        <input type='button' onclick='visiblePost(<?=$r_post["id"]?>, "<?=$title?>", 0);' value='Unpublish'/>
+<?php
+                                    }
+                                    else{
+?>
+                                        <input type='button' onclick='visiblePost(<?=$r_post["id"]?>, "<?=$title?>", 1);' value='Publish'/>
+<?php
+                                    }
+?>
+                                </td>
+                            </tr>
+<?php
+                        }
+?>
+                    </table>
+                </div> <!-- .entry -->
+            </div> <!-- .section -->
+        </div> <!-- #content -->
+    </body>
+</html>
+<?php
+    }
+?>

+ 20 - 0
www-admin/blog/new.php

@@ -0,0 +1,20 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401);
+        header("Location: /authentication.php");
+    }
+    else{
+        $user = mysqli_real_escape_string($_SESSION['id']);
+        mysqli_query($con, "INSERT INTO post (id, permalink, title, text, user, dtime, visible) VALUES ((SELECT max(id) +1 FROM post p), concat((SELECT max(id) +1 FROM post p), ''), '', '', $user, now(), 0);");
+        $id = mysqli_fetch_array(mysqli_query($con, "SELECT max(id) AS id FROM project;"))["id"];
+        header("Location: /blog/edit.php?p=" + $id);
+    }
+?>

+ 28 - 0
www-admin/blog/uploadimage.php

@@ -0,0 +1,28 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $lang = "en";
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = $proto . substr($http_host, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401);
+        header("Location: /authentication.php");
+    }
+    else{
+        foreach ($_POST as $key => $value){
+            error_log("{$key} = {$value}");
+        }
+        $name = mysqli_real_escape_string($con, $_GET["name"]);
+        $base64string = $_POST["image"];
+        error_log("FILENAME: $name");
+        error_log("PATH: $doc_root../www/img/blog/DEBUG_$name");
+        error_log("B64: $base64string");
+        file_put_contents("$doc_root../www/img/blog/DEBUG_$name", base64_decode($base64string));
+
+        echo("OK");
+    }
+?>

+ 29 - 0
www-admin/blog/visible.php

@@ -0,0 +1,29 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401); // Not logged in: Forbidden.
+    }
+    else{
+        $id = intval(mysqli_real_escape_string($con, $_GET["p"]));
+        $visible = intval(mysqli_real_escape_string($con, $_GET["v"]));
+        if (strlen($id) < 1 || ($visible != 1 && $visible != 0)){
+            http_response_code(400); // No or bad ID, visible not 1 or 0: Bad request.
+        }
+        else{
+            if (mysqli_num_rows(mysqli_query($con, "SELECT id FROM post WHERE id = $id;")) == 0){
+                http_response_code(404); // Unexistent id: Not found.
+            }
+            else{
+                mysqli_query($con, "UPDATE post SET visible = $visible WHERE id = $id;");
+                http_response_code(200); //OK.
+            }
+        }
+    }
+?>

+ 131 - 0
www-admin/css/blog.css

@@ -0,0 +1,131 @@
+/* /blog/index.php */
+
+table#post_list{
+    width: 100%;
+    border-spacing: 0;
+    border-collapse: collapse;
+}
+
+table#post_list th{
+    font-weight: bold;
+    background: linear-gradient(to bottom, #4e9987 0%,#69d8b9 89%,#60caac 97%,#ade8f9 100%);
+    border: 0.1em solid #335533;
+}
+
+table#post_list td{
+    border: 0.1em solid #335533;
+    padding: 0.2em;
+}
+
+table#post_list td.td_post{
+    width: 30%;
+}
+
+table#post_list td.td_post h4{
+    margin: 0.2em;
+}
+
+table#post_list td.td_post h4 img{
+    max-height: 2em;
+    max-width: 2em;
+    vertical-align: middle;
+}
+
+table#post_list td.td_post img.post_image{
+    border: 0.2em solid #335533;
+    border-radius: 0.4em;
+    max-height: 3em;
+    max-width: 3em;
+}
+
+table#post_list td.td_post{
+    width: 50%;
+    font-size: 80%;
+}
+
+table#post_list td.td_actions{
+    width: 20%;
+    text-align: center;
+}
+
+table#post_list td.td_actions input{
+    padding: 0.1em;
+    display: block;
+    margin: auto;
+    width: 90%;
+}
+
+
+/* /blog/edit.php */
+
+div#post_edit hr.separator{
+    margin: 1.5em 20% 1.5em 20%;
+    opacity: 0.5;
+}
+
+div#post_edit div#image_reel{
+    overflow-x: scroll;
+    border: 0.1em solid #003400;
+    border-radius: 0.3em;
+    height: 7em;
+    background-color: #334433;
+    padding: 0.5em;
+}
+
+div#post_edit div#image_reel div.post_image{
+    display: inline-block;
+    vertical-align: top;
+    margin: 0.5em 0.25em 0.5em 0.25em;
+}
+
+div#post_edit div#image_reel div.post_image img{
+    vertical-align: middle;
+    max-height: 7em;
+    max-width: 9em;
+    border: 0.2em solid #003400;
+    border-radius: 0.1em;
+    margin: 0.5em;
+    background-color: #ffffff;
+}
+
+div#post_edit div#image_reel div.post_image .image_control{
+    display: inline-block;
+    font-size: 100%;
+    width: 40%;
+    height: 2em;
+    text-align: center;
+    vertical-align: top;
+}
+
+div#post_edit table#comments{
+    border-collapse: collapse;
+    width: 100%;
+}
+
+div#post_edit table#comments td{
+    border-right: 0.1em solid #334433;
+    border-left: 0.1em solid #334433;
+    border-bottom: 0.2em solid #334433;
+    border-top: 0.2em solid #334433;
+}
+
+div#post_edit table#comments td.text{
+    width: 35%;
+}
+
+div#post_edit table#comments td.details{
+    width: 50%;
+    line-height: 2
+}
+
+div#post_edit table#comments td.actions{
+    width: 15%;
+    text-align: center;
+}
+
+div#post_edit table#comments td.actions input{
+    width: 90%;
+    margin: auto;
+    padding: 0.2em;
+    display: block;
+}

+ 29 - 0
www-admin/css/home.css

@@ -0,0 +1,29 @@
+body{
+    text-align: center;
+}
+
+div#section_table{
+    display: table;
+    width: 100%;
+}
+
+div.section_row{
+    display: table-row;
+}
+
+div.section_cell{
+    display: table-cell;
+    width: 50%;
+}
+
+div.section{
+    display: block;
+    vertical-align: top;
+    text-align: left;
+    margin:  2em;
+}
+
+div.section#authentication{
+    display: inline-block;
+    margin: 8em auto auto auto;
+}

+ 0 - 0
www-admin/css/m/ui.css


+ 169 - 0
www-admin/css/projects.css

@@ -0,0 +1,169 @@
+/* /projects/index.php */
+
+table#project_list{
+    width: 100%;
+    border-spacing: 0;
+    border-collapse: collapse;
+}
+
+table#project_list th{
+    font-weight: bold;
+    background: linear-gradient(to bottom, #4e9987 0%,#69d8b9 89%,#60caac 97%,#ade8f9 100%);
+    border: 0.1em solid #335533;
+}
+
+table#project_list td{
+    border: 0.1em solid #335533;
+    padding: 0.2em;
+}
+
+table#project_list td.td_project{
+    width: 30%;
+}
+
+table#project_list td.td_project h4{
+    margin: 0.2em;
+}
+
+table#project_list td.td_project h4 img{
+    max-height: 2em;
+    max-width: 2em;
+    vertical-align: middle;
+}
+
+table#project_list td.td_project img.project_image{
+    border: 0.2em solid #335533;
+    border-radius: 0.4em;
+    max-height: 3em;
+    max-width: 3em;
+}
+
+table#project_list td.td_details{
+    width: 50%;
+    font-size: 80%;
+}
+
+table#project_list td.td_actions{
+    width: 20%;
+    text-align: center;
+}
+
+table#project_list td.td_actions input{
+    padding: 0.1em;
+    display: block;
+    margin: auto;
+    width: 90%;
+}
+
+
+/* /projects/edit.php */
+
+div#project_edit hr.separator{
+    margin: 1.5em 20% 1.5em 20%;
+    opacity: 0.5;
+}
+
+div#project_edit img#icon{
+    vertical-align: middle;
+    width: 4em;
+    height: 4em;
+    border: 0.3em solid #003400;
+    border-radius: 1em;
+}
+
+div#project_edit input#icon_file{
+    width: 0;
+    height: 0;
+    opacity: 0;
+}
+
+div#project_edit div#image_reel{
+    overflow-x: scroll;
+    border: 0.1em solid #003400;
+    border-radius: 0.3em;
+    height: 7em;
+    background-color: #334433;
+    padding: 0.5em;
+}
+
+div#project_edit div#image_reel img{
+    vertical-align: middle;
+    max-height: 5em;
+    max-width: 7em;
+    border: 0.2em solid #003400;
+    border-radius: 0.1em;
+    margin: 0.5em;
+    background-color: #ffffff;
+}
+
+div#project_edit input#new_version{
+    width: 16em;
+    margin: 0.5em auto 2em auto;
+    display: block;
+}
+
+div#project_edit table#versions{
+    border-collapse: collapse;
+    width: 100%;
+}
+
+div#project_edit table#versions td{
+    border-right: 0.1em solid #334433;
+    border-left: 0.1em solid #334433;
+    border-bottom: 0.2em solid #334433;
+    border-top: 0.2em solid #334433;
+}
+
+div#project_edit table#versions td.version_name{
+    width: 15%;
+}
+
+div#project_edit table#versions td.version_details{
+    width: 70%;
+    line-height: 2
+}
+
+div#project_edit table#versions td.version_actions{
+    width: 15%;
+    text-align: center;
+}
+
+div#project_edit table#versions td.version_actions input{
+    width: 90%;
+    margin: auto;
+    padding: 0.2em;
+    display: block;
+}
+
+div#project_edit table#comments{
+    border-collapse: collapse;
+    width: 100%;
+}
+
+div#project_edit table#comments td{
+    border-right: 0.1em solid #334433;
+    border-left: 0.1em solid #334433;
+    border-bottom: 0.2em solid #334433;
+    border-top: 0.2em solid #334433;
+}
+
+div#project_edit table#comments td.text{
+    width: 35%;
+}
+
+div#project_edit table#comments td.details{
+    width: 50%;
+    line-height: 2
+}
+
+div#project_edit table#comments td.actions{
+    width: 15%;
+    text-align: center;
+}
+
+div#project_edit table#comments td.actions input{
+    width: 90%;
+    margin: auto;
+    padding: 0.2em;
+    display: block;
+}

+ 5 - 0
www-admin/css/stats.css

@@ -0,0 +1,5 @@
+div.pie_chart{
+    display: inline-block;
+    vertical-align: top;
+    margin: 0.5em;
+}

+ 507 - 0
www-admin/css/ui.css

@@ -0,0 +1,507 @@
+/**********************************
+ * Fonts used across the page     *
+ **********************************/
+
+/* Font */
+@font-face {
+    font-family: 'Nunito'; 
+    src: url('/fonts/Nunito-Regular.ttf'); 
+}
+
+/**********************************
+ * Styles to be applied across    *
+ * all the site for all the       *
+ * elements of it's type          *
+ **********************************/
+
+/* Styles for the body of the page */
+body{
+    background-color: #111111;
+    padding: 0;
+}
+
+/* Style for all links */
+a{
+    color: #555555;
+    text-shadow: 0 0 0.5em #7777ff;
+    text-decoration: none;
+}
+
+/* Elements fith bold font */
+.bold{
+    font-weight: bold;
+}
+
+.pointer {
+    cursor: pointer;
+}
+
+/* Hidden elements */
+.hidden{
+    display: none;
+}
+
+img.slider{
+    width: 1em;
+    height: 1em;
+    vertial-align: middle;
+    transition: all .3s ease-in-out;
+}
+
+div#cover{
+    display: none;
+    background-color: #000000bb;
+    opacity: 0;
+    transition: opacity .3s ease-in-out;
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+div#dialog{
+    display: none;
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    opacity: 0;
+    transition: opacity .3s ease-in-out;
+    text-align: center;
+    z-index: 1;
+}
+
+div#dialog div#dialog_background{
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: #00000088;
+    z-index: -1;
+}
+
+div#dialog div.section{
+    width: 50%;
+    height: 50%;
+    margin: 5em auto auto auto;
+    z-index: 1;
+}
+
+
+/**********************************
+ * Header style                  *
+ **********************************/
+
+div#header{
+    background-color: #000000;
+    margin: 0;
+    width: 100%;
+    height: 2.6em;
+    padding: 0;
+}
+
+div#header img{
+    height: 1.8em;
+    vertical-align: center;
+}
+
+div#header table{
+    width: 100%;
+    height: 100%;
+    margin-left: auto;
+    margin-right: auto;
+    border-spacing: 0;
+    border-collapse: collapse;
+    margin: 0;
+    table-layout: fixed;
+}
+
+div#header td{
+    color: #dddddd;
+    font-weight: bold;
+    text-align: center;
+}
+
+div#header td a{
+    color: #dddddd;
+    font-weight: bold;
+    text-align: center;
+    text-shadow: none;
+    text-decoration: none;
+    transition: all .3s ease-in-out;
+}
+
+
+/******************************************
+ * Secondary header.                     *
+ ******************************************/
+div.secondary_header{
+    background-color: #333333;
+    margin: 0;
+    display: none;
+    width: 100%;
+    height: 2.6em;
+}
+
+div.secondary_header table{
+    width: 90%;
+    margin-left: auto;
+    margin-right: auto;
+    table-layout: fixed;
+}
+
+div.secondary_header td{
+    text-align: center;
+}
+
+div.secondary_header td a{
+    color: #dddddd;
+    font-weight: bold;
+    text-align: center;
+    text-shadow: none;
+    text-decoration: none;
+    transition: all .3s ease-in-out;
+}
+
+div.secondary_header td a:hover{
+    color: #dddddd;
+    text-shadow: none;
+    text-decoration: none;
+}
+
+
+/**********************************
+ * Styles for the elements that   *
+ * compose the basic strucure of  *
+ * the site.                      *
+ **********************************/
+
+/* The content is everything between the header and the footer */
+div#content{
+    position: relative;
+    width: 100%;
+    height: 100%;
+    margin-bottom: 1.875em;
+}
+
+div.section{
+    margin: 0.5em;
+    padding: 0;
+    background-color: #ffffff;
+    border: 0.063em solid #005500;
+    border-radius: 1em 1em;
+    box-shadow: 0 0 0.75em #003300;
+    overflow: hidden;
+}
+
+div.section h3.section_title{
+    font-weight: bold;
+    color: #ccddff;
+    text_align: left;
+    padding: 0.2em 0.4em 0.2em 0.4em;
+    margin: 0;
+    text-shadow: 0 0 0.4em #000022;
+    font-size: 150%;
+    background: linear-gradient(to bottom, #1e9957 0%,#29d889 89%,#20ca7c 97%,#7de8b9 100%);
+}
+
+/* .entry is a style only class to show content. Must be allways children os a .lighted element */
+div.section div.entry{
+    border: 0.1em solid #aaffaa00;
+    border-radius: 0.3em;
+    padding: 0.5em;
+    margin: 0.5em;
+    transition: all .4s ease-in-out;
+}
+
+div.section div.entry_list:hover{
+    background-color: #efffef;
+    border: 0.1em solid #aaffaaff;
+}
+
+
+
+/* Style for the mathces in search results */
+span.result_counter{
+    margin-left: 1.2em;
+    margin-bottom: 0.5em;
+    font-weight: bold;
+    font-style: italic;
+}
+
+/* Shown when no records on database */
+div.unavailable{
+    margin-left: 4em;
+    font-style: italic;
+}
+
+
+/**********************************
+ * Styles to be applied to some   *
+ * horizontally scrollable        *
+ * elements                       *
+ **********************************/
+
+/* Horizontall scrollbar that apears when a project has lots of renders */
+.slider::-webkit-scrollbar{
+    width: 0.5em;
+    height: 0.25em;
+    background-color: #444444;
+} 
+.slider::-webkit-scrollbar-thumb{
+    background-color: #cccccc;
+    border-radius: 0.714em;
+}
+.slider::-webkit-scrollbar-thumb:hover{
+    background-color: #cccccc;
+    border: 0.143em solid #000000;
+}
+.slider::-webkit-scrollbar-thumb:active{
+    background-color: #cccccc;
+    border: 0.143em solid #000000;
+}
+
+/* Icons for sharing page in a social network */
+img.social_icon{
+    width: 4.286em;
+    height: 4.286em;
+}
+
+/* Elements that should have the cursor as pointer */
+.pointer{
+    cursor: pointer;
+}
+
+
+
+/**********************************
+ * Styles for diverse input       *
+ * elements. Type-assigned        *
+ **********************************/
+
+/* Font for all the input elements */
+input{
+    font-family: 'Nunito';
+}
+
+/* Style forr buttons */
+input[type=button], input[type=submit]{
+    font-weight: bold;
+    color: #cccccc;
+    background-color: #555555;
+    border: 0.143em solid #bbbbbb;
+    border-radius: 0.429em;
+    padding: 0.5em;
+    text-shadow: 0 0 0.75em #000000;
+    cursor: pointer;
+}
+
+/* Style for textboxes */
+input[type=text], input[type=password]{
+    font-weight: bold;
+    color: #224422;
+    background-color: #bbccbb;
+    border: 0.286em solid #ddeedd; 
+    border-color: #dddddd;
+    border-radius: 0.429em;
+    padding: 0.25em;
+    text-shadow: 0 0 0.5em #ffffff;
+}
+
+input[type=checkbox]{
+    width: 1em;
+    height: 1em;
+    border-radius: 0.2em;
+    border: 0.063em solid #005500;
+}
+
+/* Style for textareas */
+textarea{
+    width: 80%;
+    font-weight: bold;
+    color: #444444;
+    background-color: #bbbbbb;
+    border: 0.286em solid #dddddd; 
+    border-color: #dddddd;
+    border-radius: 0.286em;
+    padding: 0.25em;
+    text-shadow: 0 0 0.5em #ffffff;
+    resize: none;
+}
+
+/* Style for selects */
+select{
+    font-weight: bold;
+    color: #444444;
+    background-color: #bbbbbb;
+    border: 0.286em solid #dddddd; 
+    border-radius: 0.286em;
+    padding: 0.25em;
+    text-shadow: 0 0 0.5em #ffffff;
+}
+
+/* Styles so the input field to upload an image with the comment looks like a button */
+label.image_upload {
+    font-family: 'Nunito';
+    font-weight: bold;
+    color: #cccccc;
+    background-color: #555555;
+    border: 0.143em solid #bbbbbb;
+    border-radius: 0.429em;
+    padding: 0.5em;
+    text-shadow: 0 0 0.75em #000000;
+    cursor: pointer;
+}
+
+label.image_upload input {
+    width: 0 !important;
+    opacity: 0 !important;
+    overflow: hidden !important;
+}
+
+/* Previsualization for the upload image in a comment */
+img#new_comment_image_preview{
+        max-height: 7.692em;
+        max-width: 11.538em;
+}
+
+/* Style for placeholders in input fields */
+::-webkit-input-placeholder { /* WebKit browsers */
+    color: #777777;
+}
+:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
+    color: #777777;
+}
+::-moz-placeholder { /* Mozilla Firefox 19+ */
+    color: #777777;
+}
+:-ms-input-placeholder { /* Internet Explorer 10+ */
+    color: #777777;
+}
+
+/* Style-only class user to indicate that a field is incorrect. Ususally this class is added to an element with js */
+.error{
+    border-color: #ffaaaa;
+}
+
+/* Style for the textbox input when the entered text is not the expected one */
+input[type=text].error, input[type=password].error{
+    border-color: #ffaaaa;
+}
+
+/* Style for the textared input when the entered text is not the expected one */
+textarea.error{
+    border-color: #ffaaaa;
+}
+
+/* Style for buttons that act as links */
+a.a_button{
+    display: inline-block;
+    color: #cccccc;
+    background-color: #555555;
+    border: 0.143em solid #bbbbbb;
+    border-radius: 0.429em;
+    padding: 0.5em;
+    text-shadow: 0 0 0.75em #000000;
+    text-decoration: none;
+    font: -webkit-small-control;
+    font-family: 'Nunito';
+    font-weight: bold;
+}
+
+
+
+/**********************************
+ * Styles for editable items  .   *
+ * horizontally scrollable        *
+ * elements                       *
+ **********************************/
+
+/* Field value, not editing */
+.editable .field_result{
+    display: initial;
+}
+
+/* Button to start editing*/
+.editable .button_edit{
+    display: initial;
+    cursor: pointer;
+    max-width: 1.2em;
+    vertical-align: middle;
+    opacity: 0.5;
+}
+
+/* Field value, editing. */
+.editable .field_editable{
+    display: none;
+}
+
+/* Field value, editing, textarea <-> cke */
+.editable .cke{
+    display: none;
+}
+
+/* Button to save edition. */
+.editable .button_save{
+    display: none;
+    cursor: pointer;
+    max-width: 1.2em;
+    vertical-align: middle;
+}
+
+/* Button to save edition. */
+.editable .button_cancel{
+    display: none;
+    cursor: pointer;
+    max-width: 1.2em;
+    vertical-align: middle;
+}
+
+/******************************************
+* Language selectors                      *
+******************************************/
+
+div#lang_tabs{
+    margin-top: 2em;
+    margin-bottom: 2em;
+    text-align: center;
+}
+
+div#lang_tabs table{
+    border-collapse: collapse;
+    border: 0.2em solid #1e9957;
+    border-radius: 0.2em;
+    margin: 0.5em auto 1em auto;
+}
+
+div#lang_tabs td{
+    background-color: #ffffff;
+    color: #29d889;
+    font-weight: bold;
+    width: 7em;
+    margin: 0;
+    padding: 0.2em;
+    border: 0.05em solid #1e9957;
+}
+
+div#lang_tabs td.lang_tabs_active{
+    background-color: #29d889;
+    color: #ffffff;
+}
+
+div.content_lang{
+    display: none;
+    margin: 0;
+    padding: 0;
+}
+
+div.content_lang_active{
+    display: initial;
+}

BIN
www-admin/fonts/Nunito-Bold.ttf


BIN
www-admin/fonts/Nunito-Light.ttf


BIN
www-admin/fonts/Nunito-Regular.ttf


+ 93 - 0
www-admin/fonts/OFL.txt

@@ -0,0 +1,93 @@
+Copyright (c) 2011, Vernon Adams (vern@newtypography.co.uk),
+with Reserved Font Name Nunito.
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.

+ 354 - 0
www-admin/functions.php

@@ -0,0 +1,354 @@
+<?php
+
+    $IMG_SIZE_PREVIEW = 600;
+    $IMG_SIZE_MINIATURE = 200;
+
+
+    /*****************************************************
+     * This function is called from almost everywhere at *
+     * the beggining of the page. It initializes the     *
+     * session variables, connect to the db, enabling    *
+     * the variable $con for futher use everywhere in    *
+     * the php code, and populates the arrays $user      *
+     * and $permission, with info about the user.        *
+     *                                                   *
+     * @return: (db connection): The connection handler. *
+     *****************************************************/
+    function start_db(){
+        //Include the db configuration file. It's somehow like this
+        /*
+        <?php
+            $db_host = "XXXX";
+            $db_name = "XXXX";
+            $db_user = "XXXX";
+            $db_pass = "XXXX";
+        ?>
+        */
+        include ".htpasswd";
+
+        $con = mysqli_connect($db_host, $db_user, $db_pass, $db_name);
+
+        // Check connection
+        if (mysqli_connect_errno()){
+            error_log("Failed to connect to database: " . mysqli_connect_error());
+            return -1;
+        }
+
+        //Set encoding options
+        mysqli_set_charset($con, "utf-8");
+        header("Content-Type: text/html; charset=utf8");
+        mysqli_query($con, "SET NAMES utf8;");
+
+        //Return the db connection
+        return $con;
+    }
+
+
+    /*****************************************************
+     * Finds out the protocol the user is connecting to  *
+     * the site with (http or https)                     *
+     * @return: (string): "http://" or "https://".       *
+     ****************************************************/
+    function get_protocol(){
+        if (isset($_SERVER["HTTPS"]) && ($_SERVER["HTTPS"] == "on" || $_SERVER["HTTPS"] == 1) || isset($_SERVER["HTTP_X_FORWARDED_PROTO"]) && $_SERVER["HTTP_X_FORWARDED_PROTO"] == "https") {
+            $protocol = "https://";
+        }
+        else {
+            $protocol = "http://";
+        }
+        return $protocol;
+    }
+
+
+    /****************************************************
+    * This function turns a date string into a human    *
+    * readable string, deppending o the specified       *
+    * language.                                         *
+    * @params:                                          *
+    *    strdate: (string): Date string.                *
+    *    lang: (string): Language code (es, en, eu).    *
+    *    http_accept_language: (string) Raw header with *
+    *                          info about client        *
+    *                          language preferences.    *
+    *    time: (boolean): Append the time at the end.   *
+    * @return: (string array) List of the languages     *
+    *          offered, sorted by prefference.          *
+    ****************************************************/
+    function format_date($strdate, $lang, $time = true){
+        $date = strtotime($strdate);
+        $year = date('o', $date);
+        $month = date('n', $date);
+        $month --;
+        $day = date('j', $date);
+        $wday = date('N', $date);
+        $wday --;
+        $hour = date('H', $date);
+        $minute = date('i', $date);
+        switch ($lang){
+            case 'en':
+                $week = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
+                $months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+                if ($time){
+                    $str = "$week[$wday], $months[$month] $day, $year at $hour:$minute";
+                }
+                else{
+                    $str = "$week[$wday], $months[$month] $day, $year";
+                }
+                break;
+            case 'eu':
+                $week = ['astelehena', 'asteartea', 'asteazkena', 'osteguna', 'ostirala', 'larumbata', 'igandea'];
+                $months = ['urtarrilaren', 'otsailaren', 'martxoaren', 'apirilaren', 'maiatzaren', 'ekainaren', 'uztailaren', 'abuztuaren', 'irailaren', 'urriaren', 'azaroaren', 'abenduaren'];
+                if ($time){
+                    $str = $year . "ko $months[$month] $day" . "an, $week[$wday], $hour:$minute";
+                }
+                else{
+                    $str = $year . "ko $months[$month] $day" . "an, $week[$wday]";
+                }
+                break;
+            default:
+                $week = ['Lunes', 'Martes', 'Mi&eacute;rcoles', 'Jueves', 'Viernes', 'S&aacute;bado', 'Domingo'];
+                $months = ['enero', ' febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'];
+                if ($time){
+                    $str = "$week[$wday] $day de $months[$month] de $year a las $hour:$minute";
+                }
+                else{
+                    $str = "$week[$wday] $day de $months[$month] de $year";
+                }
+        }
+        return $str;
+    }
+
+
+    /*****************************************************
+     * Generates a URL-valid string from a regular one.  *
+     *                                                   *
+     * @params:                                          *
+     *    text: (string): Original string.               *
+     * @return: (string): URL-valid string.              *
+     *****************************************************/
+    function permalink($text){
+        $unwanted_array = array("Š"=>"S", "š"=>"s", "Ž"=>"Z", "ž"=>"z", "À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"A", "Ç"=>"C", "È"=>"E", "É"=>"E",
+                            "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I", "Î"=>"I", "Ï"=>"I", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O", "Õ"=>"O", "Ö"=>"O", "Ø"=>"O", "Ù"=>"U",
+                            "Ú"=>"U", "Û"=>"U", "Ü"=>"U", "Ý"=>"Y", "Þ"=>"B", "ß"=>"Ss", "à"=>"a", "á"=>"a", "â"=>"a", "ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"a", "ç"=>"c",
+                            "è"=>"e", "é"=>"e", "ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", "ï"=>"i", "ð"=>"o", "ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o",
+                            "ö"=>"o", "ø"=>"o", "ù"=>"u", "ú"=>"u", "û"=>"u", "ý"=>"y", "þ"=>"b", "ÿ"=>"y" );
+        $str = strtr( $text, $unwanted_array );
+        $str = preg_replace("/[^\da-zA-Z ]/i", "", $str);
+        $str = str_replace(" ", "-", $str);
+        return $str;
+    }
+
+
+    /*****************************************************
+     * Retrieves a text fragment from the database.      *
+     *                                                   *
+     * @params:                                          *
+     *    con (Mysql connection): Connection handler.    *
+     *    id: (string): Text identifier.                 *
+     *    lang: (string): Language identifier.           *
+     * @return: (string): Text.                          *
+     *****************************************************/
+    function text($con, $id, $lang){
+        $q = mysqli_query($con, "SELECT text, file FROM text WHERE id = '$id' AND lang = '$lang';");
+        $r = mysqli_fetch_array($q);
+        if (strlen($r["file"]) > 0){
+            return file_get_contents($_SERVER["DOCUMENT_ROOT"] . "string/" . $r["file"]);
+        }
+        else{
+            return $r["text"];
+        }
+    }
+
+
+    /*****************************************************
+     * This function closes all the opened HTML tags in  *
+     * a given string.                                   *
+     *                                                   *
+     * @params:                                          *
+     *    html: (string): The string with HTML tags.     *
+     * @return: (string): HTML with closed tags.         *
+     *****************************************************/
+    function close_tags($html) {
+        preg_match_all("#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU", $html, $result);
+        $openedtags = $result[1];
+        preg_match_all("#</([a-z]+)>#iU", $html, $result);
+        $closedtags = $result[1];
+        $len_opened = count($openedtags);
+        if (count($closedtags) == $len_opened) {
+            return $html;
+        }
+        $openedtags = array_reverse($openedtags);
+        for ($i=0; $i < $len_opened; $i++) {
+            if (!in_array($openedtags[$i], $closedtags)) {
+                $html .= "</".$openedtags[$i].">";
+            } else {
+                unset($closedtags[array_search($openedtags[$i], $closedtags)]);
+            }
+        }
+        return $html;
+    }
+
+
+
+    /*****************************************************
+     * Text shortener. Given a string, it trims in the   *
+     * proximity of the desired string, ut to the next   *
+     * white character. If indicated, it will append a   *
+     * link to the full text.                            *
+     *                                                   *
+     * @params:                                          *
+     *    text: (string): The text to shorten.           *
+     *    length: (int): The desired length.             *
+     *    linktext: (string): Text fot the link.         *
+     *    link: (string): URI of the full text.          *
+     * @return: (string): Cutted text.                   *
+     *****************************************************/
+     function cut_text($text, $length, $linktext, $link){
+        if (strlen($text) < $length){
+            return $text;
+        }
+        $cut = substr($text, 0, strpos($text, " ", $length));
+        $cut = close_tags($cut);
+        if (strlen($cut) == 0){
+            $cut = $text;
+        }
+        if (strlen($text) != strlen($cut)){
+            $cut = $cut . "... <a href='$link'>$linktext</a>";
+        }
+        return $cut;
+    }
+
+
+    /*****************************************************
+     * Finds out the IP address of the client.           *
+     *                                                   *
+     * @return: (String): Client IP address.             *
+     *****************************************************/
+    function get_ip(){
+        $client  = @$_SERVER["HTTP_CLIENT_IP"];
+        $forward = @$_SERVER["HTTP_X_FORWARDED_FOR"];
+        $remote  = $_SERVER["REMOTE_ADDR"];
+        if(filter_var($client, FILTER_VALIDATE_IP)){
+            $ip = $client;
+        }
+        elseif(filter_var($forward, FILTER_VALIDATE_IP)){
+            $ip = $forward;
+        }
+        else{
+            $ip = $remote;
+        }
+        return $ip;
+    }
+
+
+    /*****************************************************
+     * Tries to login a user to the internal page.       *
+     *                                                   *
+     * @params:                                          *
+     *    con (Mysql connection): Connection handler.    *
+     *    user (string): Username, plain text.           *
+     *    pass (string): Password SHA-1 sum.             *
+     * @return: (boolean): True if user pass/match,      *
+     *          false otherwise.                         *
+     *****************************************************/
+    function login($con, $user, $pass){
+        session_start();
+        $q = mysqli_query($con,"SELECT id, salt, username AS username, sha1(salt) AS s FROM user WHERE (lower(username) = lower('$user') OR lower(email) = lower('$user')) AND password = sha1(concat('$pass', sha1(salt)));");
+        if (mysqli_num_rows($q) == 1){
+            $r = mysqli_fetch_array($q);
+            $_SESSION['id'] = $r['id'];
+            $_SESSION['salt'] = $r['s'];
+            $_SESSION['name'] = $r['username'];
+            return true;
+        }
+        else{
+            error_log("Invalid login. username = $user.");
+            return false;
+        }
+    }
+
+
+    /*****************************************************
+     * Checks if a user is logged in.                    *
+     *                                                   *
+     * @params:                                          *
+     *    con (Mysql connection): Connection handler.    *
+     * @return: (boolean): True if the user is signed    *
+     *          in, false otherwise.                     *
+     *****************************************************/
+    function check_session($con){
+        session_start(['cookie_lifetime' => 1800,]);
+        $qr = mysqli_query($con, "SELECT id FROM user WHERE id = '$_SESSION[id]' AND sha1(salt) = '$_SESSION[salt]';");
+        //error_log("SELECT id FROM user WHERE id = '$_SESSION[id]' AND md5(salt) = '$_SESSION[salt]';");
+        if (mysqli_num_rows($qr) == 1)
+            return true;
+        else{
+            return false;
+            error_log("Invalid session. username = $_SESSION[name], id = $_SESSION[id]");
+        }
+    }
+
+
+    /*****************************************************
+     * Performs an update in the database.               *
+     *                                                   *
+     * @params:                                          *
+     *    con (Mysql connection): Connection handler.    *
+     *    s_table (string): Table name.                  *
+     *    s_column (string): Column name.                *
+     *    type (string): Column type (VARCHAR, NUMBER,   *
+     *         DATE or TIME. If the value to insert in   *
+     *         null, type must be NULL).                 *
+     *    s_value (string): New value. Ignoered if type  *
+     *            is NULL.                               *
+     *    id (string): Row id. If the table doesn't have *
+     *       a column named 'id', this can't be used.    *
+     *****************************************************/
+    function db_update($con, $s_table, $s_column, $type, $s_value, $id){
+        $table = mysqli_real_escape_string($con, $s_table);
+        $column = mysqli_real_escape_string($con, $s_column);
+        switch (strtoupper($type)){
+            case "NULL":
+                $value = "null";
+                break;
+            case "VARCHAR":
+                $value = "'" . mysqli_real_escape_string($con, $s_value) . "'";
+                break;
+            case "NUMBER":
+                $value = intval(mysqli_real_escape_string($con, $s_value));
+                break;
+            case "DATE":
+                if (!preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) \d{2}:\d{2}:\d{2}$/",$s_value)){
+                    return -2;
+                }
+                $value = "str_to_date('$s_value', '%Y-%m-%d')";
+                break;
+            case "TIME":
+                if (!preg_match("^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$",$s_value)){
+                    return -2;
+                }
+                $value = "str_to_date('$s_value', '%Y-%m-%d %H:%i:%s')";
+                break;
+            default:
+                return -1;
+        }
+        $q = mysqli_query($con, "UPDATE $table SET $column = $value WHERE id = $id;");
+        return 0;
+    }
+
+
+    /*****************************************************
+     * Performs a delete                .                *
+     *                                                   *
+     * @params:                                          *
+     *    con (Mysql connection): Connection handler.    *
+     *    s_table (string): Table name.                  *
+     *    id (string): Row id. If the table doesn't have *
+     *       a column named 'id', this can't be used.    *
+     *****************************************************/
+    function db_delete($con, $s_table, $id){
+        $table = mysqli_real_escape_string($con, $s_table);
+        $q = mysqli_query($con, "DELETE FROM $table WHERE id = $id;");
+        return 0;
+    }
+?>

+ 37 - 0
www-admin/functions/save.php

@@ -0,0 +1,37 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401);
+        header("Location: /authentication.php");
+    }
+    else{
+        $table = mysqli_real_escape_string($con, $_GET["t"]);
+        $column = mysqli_real_escape_string($con, $_GET["c"]);
+        $value = htmlspecialchars_decode(mysqli_real_escape_string($con, $_GET["v"]));
+        $id = mysqli_real_escape_string($con, $_GET["i"]);
+        // TODO: Validation!
+        if ($table == 'text'){
+            if (mysqli_query($con, "UPDATE text SET text = '$value' WHERE id = '$id' AND lang = '$column';")){
+                http_response_code(200); // OK.
+            }
+            else{
+                http_response_code(500); // Generic internal error.
+            }
+        }
+        else{
+            if (mysqli_query($con, "UPDATE $table SET $column = '$value' WHERE id = $id;")){
+                http_response_code(200); // OK.
+            }
+            else{
+                http_response_code(500); // Generic internal error.
+            }
+        }
+    }
+?>

+ 87 - 0
www-admin/header.php

@@ -0,0 +1,87 @@
+<div id='dialog'>
+    <div id='dialog_background'>
+    </div>
+    <div class='section'>
+        <h3 id='dialog_title' class='section_title'></h3>
+        <div class='entry'>
+            <p id='dialog_text'></p>
+            <div id='dialog_button_a_container'>
+                <input type='button' id='dialog_accept' value='Aceptar'/>
+            </div>
+            <div id='dialog_button_yn_container'>
+                <input type='button' id='dialog_yes' value='Yes'/>
+                <input type='button' id='dialog_no' value='No'/>
+            </div>
+        </div> <!-- .entry -->
+    </div> <!-- .section -->
+</div>
+<div id='header'>
+    <table>
+        <tr>
+            <td>
+                <img src='<?=$server?>/img/misc/logo.png'/>
+                <span style='font-weight:normal;font-style:italic;font-size:90%;'><?=$_SESSION['name']?></span>
+            </td>
+            <td>
+                <a href='<?=$server?>/' class='pointer header_section'>Inicio</a>
+            </td>
+            <td class='pointer header_section' onClick='showHeader("blog", this);'>
+                <span class='pointer'>Blog</span>
+            </td>
+            <td class='pointer header_section' onClick='showHeader("projects", this);'>
+                <span class='pointer'>Projects</span>
+            </td>
+            <td class='pointer header_section' onClick='showHeader("stats", this);'>
+                <span class='pointer'>Stats</span>
+            </td>
+            <td class='pointer header_section' onClick='showHeader("settings", this);'>
+                <span class='pointer'>Settings</span>
+            </td>
+        </tr>
+    </table>
+</div>
+<div class='secondary_header' id='header_blog'>
+    <table>
+        <tr>
+            <td>
+                <a href='<?=$server?>/blog/add/'>New post</a>
+            </td>
+            <td>
+                <a href='<?=$server?>/blog/'>Manage posts</a>
+            </td>
+        </tr>
+    </table>
+</div>
+<div class='secondary_header' id='header_projects'>
+    <table>
+        <tr>
+            <td>
+                <a href='<?=$server?>/projects/add/'>New project</a>
+            </td>
+            <td>
+                <a href='<?=$server?>/projects/'>Manage projects</a>
+            </td>
+        </tr>
+    </table>
+</div>
+<div class='secondary_header' id='header_stats'>
+    <table>
+        <tr>
+            <td>
+                <a href='<?=$server?>/stats/'>View stats</a>
+            </td>
+        </tr>
+    </table>
+</div>
+<div class='secondary_header' id='header_settings'>
+    <table>
+        <tr>
+            <td>
+                <a href='<?=$server?>/settings/'>Page settings</a>
+            </td>
+            <td>
+                <a href='<?=$server?>/settings/profile.php'>Profile settings</a>
+            </td>
+        </tr>
+    </table>
+</div>

BIN
www-admin/img/icon/cancel.png


BIN
www-admin/img/icon/edit.png


BIN
www-admin/img/icon/save.png


+ 101 - 0
www-admin/index.php

@@ -0,0 +1,101 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        header("Location: /authentication.php");
+        exit(-1);
+    }
+    else{
+?>
+<html>
+    <head>
+        <meta content='text/html; charset=windows-1252' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'>
+        <link rel='shortcut icon' href='<?=$pserver?>/img/logo/x2/favicon.ico'/>
+        <title>I&ntilde;igo Valentin - Administration Panel</title>
+        <style>
+<?php
+            include $doc_root . "css/ui.css";
+            include $doc_root . "css/home.css";
+?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media='(max-width : 990px)'>
+<?php
+            include $doc_root . "css/m/ui.css";
+            include $doc_root . "css/m/home.css";
+?>
+        </style>
+        <!-- Script files -->
+        <script type='text/javascript'>
+<?php
+            include $doc_root . "script/ui.js";
+?>
+        </script>
+    </head>
+    <body>
+<?php
+        include $doc_root . "header.php";
+?>
+        <div id='content'>
+            <div id='section_table'>
+                <div class='section_row'>
+                    <div class='section_cell'>
+                        <div class='section'>
+                            <h3 class='section_title'>Blog</h3>
+                            <div class='entry'>
+                                <ul>
+                                    <li><a href='<?=$server?>/blog/new.php/'>New post</a></li>
+                                    <li><a href='<?=$server?>/blog/'>Manage posts</a></li>
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                    <div class='section_cell'>
+                        <div class='section'>
+                            <h3 class='section_title'>Projects</h3>
+                            <div class='entry'>
+                                <ul>
+                                    <li><a href='<?=$server?>/projects/add/'>New project</a></li>
+                                    <li><a href='<?=$server?>/projects/'>Manage projects</a></li>
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                </div> <!-- .section_row -->
+                <div class='section_row'>
+                    <div class='section_cell'>
+                        <div class='section'>
+                            <h3 class='section_title'>Stats</h3>
+                            <div class='entry'>
+                                <ul>
+                                    <li><a href='<?=$server?>/stats/'>View stats</a></li>
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                    <div class='section_cell'>
+                        <div class='section'>
+                            <h3 class='section_title'>Settings</h3>
+                            <div class='entry'>
+                                <ul>
+                                    <li><a href='<?=$server?>/settings/'>Page settings</a></li>
+                                    <li><a href='<?=$server?>/settings/profile.php'>Profile settings</a></li>
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                </div> <!-- .section_row -->
+            </div> <!-- #section_table -->
+        </div> <!-- #content -->
+    </body>
+</html>
+<?php
+    }
+?>

+ 14 - 0
www-admin/login.php

@@ -0,0 +1,14 @@
+<?php
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $con = start_db();
+    $user = mysqli_real_escape_string($con, $_POST["user"]);
+    $pass = mysqli_real_escape_string($con, $_POST["pass"]);
+    $pass = sha1($pass);
+    if (login($con, $user, $pass)){
+        header("Location: /index.php");
+    }
+    else{
+        header("Location: /authentication.php");
+    }
+?>

+ 33 - 0
www-admin/projects/delete.php

@@ -0,0 +1,33 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401); // Not logged in: Forbidden.
+    }
+    else{
+        $id = intval(mysqli_real_escape_string($con, $_GET["p"]));
+        if (strlen($id) < 1){
+            http_response_code(400); // No or bad ID: Bad request.
+        }
+        else{
+            if (mysqli_num_rows(mysqli_query($con, "SELECT id FROM project WHERE id = $id;")) == 0){
+                http_response_code(404); // Unexistent id: Not found.
+            }
+            else{
+                mysqli_query($con, "DELETE FROM project_version WHERE project = $id;");
+                mysqli_query($con, "DELETE FROM project_url WHERE project = $id;");
+                mysqli_query($con, "DELETE FROM project_tag WHERE project = $id;");
+                mysqli_query($con, "DELETE FROM project_image WHERE project = $id;");
+                mysqli_query($con, "DELETE FROM project_comment WHERE project = $id;");
+                mysqli_query($con, "DELETE FROM project WHERE id = $id;");
+                http_response_code(200); //OK.
+            }
+        }
+    }
+?>

+ 28 - 0
www-admin/projects/deletecomment.php

@@ -0,0 +1,28 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401); // Not logged in: Forbidden.
+    }
+    else{
+        $id = intval(mysqli_real_escape_string($con, $_GET["i"]));
+        if (strlen($id) < 1){
+            http_response_code(400); // No or bad ID: Bad request.
+        }
+        else{
+            if (mysqli_num_rows(mysqli_query($con, "SELECT id FROM project_comment WHERE id = $id;")) == 0){
+                http_response_code(404); // Unexistent id: Not found.
+            }
+            else{
+                mysqli_query($con, "DELETE FROM project_comment WHERE id = $id;");
+                http_response_code(200); //OK.
+            }
+        }
+    }
+?>

+ 28 - 0
www-admin/projects/deleteversion.php

@@ -0,0 +1,28 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401); // Not logged in: Forbidden.
+    }
+    else{
+        $id = intval(mysqli_real_escape_string($con, $_GET["v"]));
+        if (strlen($id) < 1){
+            http_response_code(400); // No or bad ID: Bad request.
+        }
+        else{
+            if (mysqli_num_rows(mysqli_query($con, "SELECT id FROM project_version WHERE id = $id;")) == 0){
+                http_response_code(404); // Unexistent id: Not found.
+            }
+            else{
+                mysqli_query($con, "DELETE FROM project_version WHERE id = $id;");
+                http_response_code(200); //OK.
+            }
+        }
+    }
+?>

+ 421 - 0
www-admin/projects/edit.php

@@ -0,0 +1,421 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $lang = "en";
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = $proto . substr($http_host, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401);
+        header("Location: /authentication.php");
+    }
+    else{
+        $id = intval(mysqli_real_escape_string($con, $_GET["p"]));
+        if (strlen($id) < 1){
+            http_response_code(400); // No or bad ID: Bad request.
+            header("Location: /projects/");
+        }
+        else{
+            $q_project = mysqli_query($con, "SELECT * FROM project WHERE id = $id;");
+            if (mysqli_num_rows($q_project) == 0){
+                http_response_code(404); // Unexistent id: Not found.
+                header("Location: /projects/");
+            }
+            else{
+                $r_project = mysqli_fetch_array($q_project);
+                $id = $r_project["id"];
+                $title = text($con, $r_project["title"], $lang);
+                $q_lang = mysqli_query($con, "SELECT * FROM lang;");
+?>
+<html>
+    <head>
+        <meta content='text/html; charset=windows-1252' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'>
+        <link rel='shortcut icon' href='<?=$pserver?>/img/logo/x2/favicon.ico'/>
+        <title><?=$title?>I&ntilde;igo Valentin - Administration Panel</title>
+        <style>
+<?php
+            include $doc_root . "css/ui.css";
+            include $doc_root . "css/projects.css";
+?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media='(max-width : 990px)'>
+<?php
+            include $doc_root . "css/m/ui.css";
+            include $doc_root . "css/m/projects.css";
+?>
+        </style>
+        <!-- Script files -->
+        <script type='text/javascript'>
+<?php
+            include $doc_root . "script/ui.js";
+            include $doc_root . "script/projects.js";
+?>
+        </script>
+         <script src='/script/ckeditor/ckeditor.js'></script>
+    </head>
+    <body>
+<?php
+        include $doc_root . "header.php";
+?>
+        <div id='content'>
+            <div class='section' id='project_edit'>
+                <h3 class='section_title'><?=$title?></h3>
+                <div class='entry'>
+                    <div id='lang_tabs'>
+                        <table>
+                            <tr>
+<?php
+                                mysqli_data_seek($q_lang, 0);
+                                $active = 'lang_tabs_active';
+                                while ($r_lang = mysqli_fetch_array($q_lang)){
+?>
+                                    <td class='pointer <?=$active?>' id='lang_tab_<?=$r_lang["code"]?>' onclick='showLanguage("<?=$r_lang["code"]?>");'>
+                                        <?=$r_lang["name"]?>
+                                    </td>
+<?php
+                                    $active = '';
+                                }
+?>
+                            </tr>
+                        </table>
+                    </div> <!-- #lang_tabs -->
+                    <span class='bold'>URL:</span>
+                    <?=$pserver?>/projects/
+                    <span class='editable'>
+                        <span class='field_result'><?=$r_project["permalink"]?></span>
+                        <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                        <input class='field_editable' type='text' value='<?=$r_project["permalink"]?>'/>
+                        <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "project", "permalink", <?=$id?>);'/>
+                        <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                    </span>
+                    <hr class='separator'/>
+                    <span class='bold'>Icon:</span>
+<?php
+                    if (strlen($r_project["icon"]) > 0){
+                        $src = "$pserver/img/projects/x3/$r_image[image]";
+                    }
+                    else{
+                        $src = "$server/img/icon/addimage.png";
+                    }
+?>
+                    <img class='pointer' onClick='selectIcon();' id='icon' src='<?=$src?>' />
+                    <input id='icon_file' type='file' accept='image/*' onchange='changeIcon(event, "<?=$title?>");'>
+                    <hr class='separator'/>
+<?php
+                    mysqli_data_seek($q_lang, 0);
+                    $active = "content_lang_active";
+                    while ($r_lang = mysqli_fetch_array($q_lang)){
+?>
+                        <div class='content_lang_<?=$r_lang["code"]?> content_lang <?=$active?>'>
+                            <div class='editable'>
+                                <span class='bold'>Title (<?=$r_lang["name"]?>):</span>
+                                <span class='field_result'><?=text($con, $r_project["title"], $r_lang["code"])?></span>
+                                <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                <input class='field_editable' type='text' value='<?=text($con, $r_project["title"], $r_lang["code"])?>'/>
+                                <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "text", "<?=$r_lang["code"]?>", "<?=$r_project["title"]?>");'/>
+                                <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                            </div>
+                            <hr class='separator'/>
+                            <div class='editable'>
+                                <span class='bold'>Summary (<?=$r_lang["name"]?>):</span>
+                                <span class='field_result'><?=text($con, $r_project["summary"], $r_lang["code"])?></span>
+                                <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                <input class='field_editable' type='text' value='<?=text($con, $r_project["summary"], $r_lang["code"])?>'/>
+                                <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "text", "<?=$r_lang["code"]?>", "<?=$r_project["summary"]?>");'/>
+                                <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                            </div>
+                            <hr class='separator'/>
+                            <div class='editable'>
+                                <span class='bold'>Text (<?=$r_lang["name"]?>):</span>
+                                <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "text", "<?=$r_lang["code"]?>", "<?=$r_project["text"]?>");'/>
+                                <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                                <br/>
+                                <p class='field_result'><?=text($con, $r_project["text"], $r_lang["code"])?></p>
+                                <textarea id='text_<?=$r_lang["code"]?>' class='field_editable'><?=text($con, $r_project["text"], $r_lang["code"])?></textarea>
+                                <script>CKEDITOR.replace('text_<?=$r_lang["code"]?>');</script>
+                            </div>
+                        </div> <!-- .project_add_language -->
+<?php
+                        $active = '';
+                    }
+?>
+                    <hr class='separator'/>
+                    <h4>Images</h4>
+                    <div id='image_reel' class='horizontal_scroll'>
+<?php
+                        $q_image = mysqli_query($con, "SELECT id, image FROM project_image WHERE project = $id;");
+                        while ($r_image = mysqli_fetch_array($q_image)){
+?>
+                            <span id='project_image_<?=$r_image["id"]?>'>
+                                <img alt='<?=$title?>' title='<?=$title?>' src='<?=$pserver?>/img/projects/x3/<?=$r_image["image"]?>' />
+                                <img class='pointer' onClick='deleteImage(<?=$r_image["id"]?>)' alt='Delete image' title='Delete image' src='<?=$server?>/img/icon/cancel.png' />
+                            </span>
+<?php
+                        }
+?>
+                        <span id='project_image_add'>
+                            <img onClick='addImage(<?=$id?>)' alt='Add image' title='Add image' src='<?=$server?>/img/icon/addimage.png' />
+                        </span> <!-- #image_reel -->
+                    </div>
+                    <hr class='separator'/>
+                    <h4>Tags</h4>
+                    TODO
+                    <hr class='separator'/>
+                    <h4>Versions</h4>
+                    <div id='versions'>
+                        <input id='new_version' type='button' value='New version' onClick='newVersion(<?=$id?>);' />
+<?php
+                        // TODO replace id -> project_version: id
+                        $q_version = mysqli_query($con, "SELECT project_version.id AS version, version_code, version_name, type, (SELECT text FROM text WHERE id = project_version_type.title AND lang = '$lang') AS type_name, color AS type_color, project_version.summary AS summary, changelog FROM project_version, project_version_type WHERE type = project_version_type.id AND project = $id ORDER BY version_code;");
+                        while ($r_version = mysqli_fetch_array($q_version)){
+?>
+                            <div class='entry list_enty'>
+                                <table id='versions'>
+                                    <tr>
+                                        <td class='version_name'>
+                                            <span class='editable'>
+                                                <span class='field_result'><?=$r_version["version_name"]?></span>
+                                                <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                                <input class='field_editable' type='text' value='<?=$r_version["version_name"]?>'/>
+                                                <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "project_version", "version", "<?=$r_version["id"]?>");'/>
+                                                <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                                            </span>
+                                        </td>
+                                        <td class='version_details'>
+                                            <span class='bold'>Version code (fixed):</span> <?=$r_version["version_code"]?>
+                                            <br/>
+                                            <span class='editable'>
+                                                <span class='bold'>Version type:</span>
+                                                <span class='field_result'><?=$r_version["type_name"]?></span>
+                                                <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                                <select class='field_editable'>
+<?php
+                                                    $q_version_type = mysqli_query($con, "SELECT id, title, color, (SELECT text FROM text WHERE id = project_version_type.title AND lang = '$lang') AS title FROM project_version_type;");
+                                                    while ($r_version_type = mysqli_fetch_array($q_version_type)){
+                                                        if ($r_version_type["id"] == $r_version["type"]){
+                                                            $selected = "selected";
+                                                        }
+                                                        else{
+                                                            $selected = "";
+                                                        }
+                                                        // TODO: Ignoring default 'selected'. It's because display: none?
+?>
+                                                        <option value='<?=$r_version_type["id"]?>' <?=$selected?>><?=$r_version_type["title"]?></option>
+<?php
+                                                    }
+?>
+                                                </select>
+                                                <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "project_version", "type", "<?=$r_version["id"]?>");'/>
+                                                <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                                            </span>
+<?php
+                                            mysqli_data_seek($q_lang, 0);
+                                            $active = "content_lang_active";
+                                            while ($r_lang = mysqli_fetch_array($q_lang)){
+?>
+                                                <div class='content_lang_<?=$r_lang["code"]?> content_lang <?=$active?>'>
+                                                    <div class='editable'>
+                                                        <span class='bold'>Summary (<?=$r_lang["name"]?>):</span>
+                                                        <span class='field_result'><?=text($con, $r_version["summary"], $r_lang["code"])?></span>
+                                                        <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                                        <input class='field_editable' type='text' value='<?=text($con, $r_project["summary"], $r_lang["code"])?>'/>
+                                                        <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "text", "<?=$r_lang["code"]?>", "<?=$r_version["summary"]?>");'/>
+                                                        <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                                                    </div>
+                                                </div>
+                                                <div class='content_lang_<?=$r_lang["code"]?> content_lang <?=$active?>'>
+                                                    <div class='editable'>
+                                                        <span class='bold'>Changelog (<?=$r_lang["name"]?>):</span>
+                                                        <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                                        <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "text", "<?=$r_lang["code"]?>", "<?=$r_version["changelog"]?>");'/>
+                                                        <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                                                        <p class='field_result'><?=text($con, $r_version["changelog"], $r_lang["code"])?></p>
+                                                        <textarea class='field_editable'><?=text($con, $r_project["summary"], $r_lang["code"])?></textarea>
+                                                    </div>
+                                                </div>
+<?php
+                                                $active = "";
+                                            }
+                                            if ($r_project["visible"] == 1){
+                                                $v = "Yes";
+                                                $selected1 = "selected";
+                                                $selected0 = "";
+                                            }
+                                            else{
+                                                $v = "No";
+                                                $selected1 = "";
+                                                $selected0 = "selected";
+                                            }
+?>
+                                            <div class='editable'>
+                                                <span class='bold'>Visible:</span>
+                                                <span class='field_result'><?=$v?></span>
+                                                <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                                <select class='field_editable'>
+                                                    <option value='1' <?=$selected1?>>Yes</option>
+                                                    <option value='0' <?=$selected0?>>No</option>
+                                                </select>
+                                                <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "project_version", "visible", "<?=$r_version["id"]?>");'/>
+                                                <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                                            </div>
+
+                                        </td>
+                                        <td class='version_actions'>
+                                            <input type='button' onclick='deleteVersion(<?=$r_version["id"]?>, "<?=$title?>");' value='Delete'/>
+                                        </td>
+                                    </tr>
+                                </table>
+                            </div> <!-- .entry -->
+<?php
+                        }
+?>
+                    </div> <!-- #versions -->
+                    <hr class='separator'/>
+                    <h4>Settings</h4>
+<?php
+                    if ($r_project["visible"] == 1){
+                        $v = "Yes";
+                        $selected1 = "selected";
+                        $selected0 = "";
+                    }
+                    else{
+                        $v = "No";
+                        $selected1 = "";
+                        $selected0 = "selected";
+                    }
+?>
+                    <div class='editable'>
+                        <span class='bold'>Visible:</span>
+                        <span class='field_result'><?=$v?></span>
+                        <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                        <select class='field_editable'>
+                            <option value='1' <?=$selected1?>>Yes</option>
+                            <option value='0' <?=$selected0?>>No</option>
+                        </select>
+                        <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "project", "visible", "<?=$id?>");'/>
+                        <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                    </div>
+<?php
+                    if ($r_project["comments"] == 1){
+                        $v = "Yes";
+                        $selected1 = "selected";
+                        $selected0 = "";
+                    }
+                    else{
+                        $v = "No";
+                        $selected1 = "";
+                        $selected0 = "selected";
+                    }
+?>
+                    <div class='editable'>
+                        <span class='bold'>Comments enabled:</span>
+                        <span class='field_result'><?=$v?></span>
+                        <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                        <select class='field_editable'>
+                            <option value='1' <?=$selected1?>>Yes</option>
+                            <option value='0' <?=$selected0?>>No</option>
+                        </select>
+                        <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "project", "comments", "<?=$id?>");'/>
+                        <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                    </div>
+                    <input type='button' onclick='deleteProject(<?=$id?>, "<?=$title?>");' value='Delete project'/>
+                    <h4>Comments</h4>
+<?php
+                        $q_comment = mysqli_query($con, "SELECT project_comment.id AS id, (SELECT version_name FROM project_version WHERE id = project_comment.version) AS version, text, dtime, user, username, lang, (SELECT name FROM lang WHERE code = project_comment.lang) AS lang_name, approved, ip, browser, os, uagent FROM project_comment, stat_visit WHERE stat_visit.id = visit AND project = $id;");
+                        if (mysqli_num_rows($q_comment) == 0){
+?>
+                            <span>No comments yet</span>
+<?php
+                        }
+                        else{
+?>
+                            <table id='comments'>
+<?php
+                            while ($r_comment = mysqli_fetch_array($q_comment)){
+?>
+                                <tr>
+                                    <td class='text'>
+                                        <p><?=$r_comment["text"]?></p>
+                                    </td>
+                                    <td class='details'>
+                                        On <?=format_date($r_comment["dtime"], $lang, true)?>
+                                        <br/>
+                                        For version <?=$r_comment["version"]?>
+                                        <br/>
+<?php
+                                        if (strlen($r_comment["user"]) > 0){
+                                            $q_user = mysqli_query($con, "SELECT concat(fname, concat(' ', lname)) AS name, username FROM user WHERE id = $r_comment[user];");
+                                            $r_user = mysqli_fetch_array($q_user);
+?>
+                                            By <?=$r_user["name"]?> (<span class='bold'><?=$r_user["username"]?></span>)
+<?php
+                                        }
+                                        else{
+?>
+                                            By <?=$r_comment["username"]?>
+<?php
+                                        }
+                                        if (strlen($r_comment["lang_name"]) > 0){
+                                            $strlang = $r_comment["lang_name"];
+                                        }
+                                        else{
+                                            $strlang = $r_comment["lang"];
+                                        }
+                                        if (file_exists($doc_root . "img/lang/" . $r_comment["lang"] . ".gif")){
+                                            $imgpath = $doc_root . "img/lang/" . $r_comment["lang"] . ".gif";
+                                            $strlang = $strlang . " <img class='lang' title='$r_comment[lang]' alt='$r_comment[lang]' src='$server/$imgpath' />";
+                                        }
+?>
+                                        <br/>
+                                        Language: <?=$strlang?>
+<?php
+                                        if ($r_project["visible"] == 1){
+                                            $v = "Yes";
+                                            $selected1 = "selected";
+                                            $selected0 = "";
+                                        }
+                                        else{
+                                            $v = "No";
+                                            $selected1 = "";
+                                            $selected0 = "selected";
+                                        }
+?>
+                                        <div class='editable'>
+                                            Approved:
+                                            <span class='field_result'><?=$v?></span>
+                                            <img class='button_edit' src='<?=$server?>/img/icon/edit.png' onclick='enableEdit(this);'/>
+                                            <select class='field_editable'>
+                                                <option value='1' <?=$selected1?>>Yes</option>
+                                                <option value='0' <?=$selected0?>>No</option>
+                                            </select>
+                                            <img class='button_save' src='<?=$server?>/img/icon/save.png' onclick='saveEdit(this, "project_comment", "approved", "<?=$r_comment["id"]?>");'/>
+                                            <img class='button_cancel' src='<?=$server?>/img/icon/cancel.png' onclick='cancelEdit(this);'/>
+                                        </div>
+                                    </td>
+                                    <td class='actions'>
+                                        <input type='button' onclick='deleteProjectComment(<?=$id?>);' value='Delete'/>
+                                    </td>
+                                </tr>
+<?php
+                            }
+?>
+                            </table>
+<?php
+                        }
+?>
+                </div> <!-- .entry -->
+            </div> <!-- .section -->
+        </div> <!-- #content -->
+    </body>
+</html>
+<?php
+            }
+        }
+    }
+?>

+ 161 - 0
www-admin/projects/index.php

@@ -0,0 +1,161 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = $proto . substr($http_host, 0, strpos($http_host, ':')); // public server
+    $lang = "en";
+    if (!check_session($con)){
+        header("Location: /authentication.php");
+        exit(-1);
+    }
+    else{
+?>
+<html>
+    <head>
+        <meta content='text/html; charset=windows-1252' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'>
+        <link rel='shortcut icon' href='<?=$pserver?>/img/logo/x2/favicon.ico'/>
+        <title>Projects - I&ntilde;igo Valentin - Administration Panel</title>
+        <style>
+<?php
+            include $doc_root . "css/ui.css";
+            include $doc_root . "css/projects.css";
+?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media='(max-width : 990px)'>
+<?php
+            include $doc_root . "css/m/ui.css";
+            include $doc_root . "css/m/projects.css";
+?>
+        </style>
+        <!-- Script files -->
+        <script type='text/javascript'>
+<?php
+            include $doc_root . "script/ui.js";
+            include $doc_root . "script/projects.js";
+?>
+        </script>
+    </head>
+    <body>
+<?php
+        include $doc_root . "header.php";
+?>
+        <div id='content'>
+            <div class='section'>
+                <h3 class='section_title'>Project list</h3>
+                <div class='entry'>
+                    <table id='project_list'>
+                        <tr>
+                            <th>Projects</th>
+                            <th>Details</th>
+                            <th>Actions</th>
+                        </tr>
+<?php
+                        $q_project = mysqli_query($con, "SELECT project.id, permalink, project_type.title AS type, project.title AS title, project.logo AS logo, header, text, license.id AS license, concat(fname, concat(' ', lname)) AS user, visible, comments, (SELECT count(id) FROM project_comment WHERE project = project.id) AS comment_count, (SELECT version_name FROM project_version v, project_version_type WHERE type = v.id AND v.project = project.id ORDER BY version_code DESC LIMIT 1) AS version, (SELECT dtime FROM project_version v WHERE  v.project = project.id ORDER BY version_code DESC LIMIT 1) AS dtime, (SELECT title FROM project_version v, project_version_type WHERE type = v.id AND v.project = project.id ORDER BY version_code DESC LIMIT 1) AS version_type, (SELECT color FROM project_version v, project_version_type WHERE type = v.id AND v.project = project.id ORDER BY version_code DESC LIMIT 1) AS version_color, (SELECT image FROM project_image WHERE project = project.id) AS image, (SELECT count(image) FROM project_image WHERE project = project.id ORDER BY idx LIMIT 1) AS image_count FROM project, project_type, license, user WHERE project.type = project_type.id AND project.license = license.id AND project.user = user.id;");
+                        while ($r_project = mysqli_fetch_array($q_project)){
+                            $title = text($con, $r_project["title"], $lang);
+?>
+                            <tr>
+                                <td class='td_project'>
+                                    <h4>
+                                        <a title='View <?=$title?> on a new tab' target='_blank' href='<?=$pserver?>/project/<?=$r_project["permalink"]?>'>
+<?php
+                                            if (strlen($r_project["logo"]) > 0){
+?>
+                                                <img alt='<?=$title?>' title='<?=$title?>' src='<?=$pserver?>/img/project/x2/<?=$r_project["logo"]?>' />
+<?php
+                                            }
+?>
+                                            <?=$title?>
+                                        </a>
+                                    </h4>
+                                    <p><?=cut_text(text($con, $r_project["header"],$lang), 300)?></p>
+<?php
+                                    if ($r_project["image_count"] > 0){
+?>
+                                        <img class='project_image' alt='<?=$title?>' title='<?=$title?>' src='<?=$pserver?>/img/project/x2/<?=$r_project["image"]?>' />
+<?php
+                                        if ($r_project["image_count"] == 2){
+?>
+                                            + other image.
+<?php
+                                        }
+                                        elseif ($r_project["image_count"] > 2){
+                                            $pl = $r_project["image_count"] - 1;
+?>
+                                            + <?=$pl?> other images.
+<?php
+                                        }
+                                    }
+?>
+                                </td>
+                                <td class='td_details'>
+                                    By <?=$r_project["user"]?>.<br/>
+                                    Last version 
+                                    <span style='color:<?=$r_project["version_color"]?>;'>
+                                        <?=$r_project["version"]?> (<?=strtolower(text($con, $r_project["version_type"], $lang))?>)
+                                    </span>
+                                    modified on <?=format_date($r_project["dtime"], $lang, false)?>.<br/>
+                                    Licensed under <?=$r_project["license"]?>.<br/>
+                                    Visible:
+<?php
+                                    if ($r_project["visible"] == 1){
+?>
+                                        Yes
+<?php
+                                    }
+                                    else{
+?>
+                                        No
+<?php
+                                    }
+?>
+                                    <br/>Comments: <?=$r_project["comment_count"]?><br/>
+                                    Comments allowed:
+<?php
+                                    if ($r_project["comments"] == 1){
+?>
+                                        Yes
+<?php
+                                    }
+                                    else{
+?>
+                                        No
+<?php
+                                    }
+?>
+                                </td>
+                                <td class='td_actions'>
+                                    <input type='button' onclick='manageProject(<?=$r_project["id"]?>);' value='Manage / Translate'/>
+                                    <input type='button' onclick='deleteProject(<?=$r_project["id"]?>, "<?=$title?>");' value='Delete'/>
+                                    <?php
+                                    if ($r_project["visible"] == 1){
+?>
+                                        <input type='button' onclick='visibleProject(<?=$r_project["id"]?>, "<?=$title?>", 0);' value='Unpublish'/>
+<?php
+                                    }
+                                    else{
+?>
+                                        <input type='button' onclick='visibleProject(<?=$r_project["id"]?>, "<?=$title?>", 1);' value='Publish'/>
+<?php
+                                    }
+?>
+                                </td>
+                            </tr>
+<?php
+                        }
+?>
+                    </table>
+                </div> <!-- .entry -->
+            </div> <!-- .section -->
+        </div> <!-- #content -->
+    </body>
+</html>
+<?php
+    }
+?>

+ 20 - 0
www-admin/projects/new.php

@@ -0,0 +1,20 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401);
+        header("Location: /authentication.php");
+    }
+    else{
+        $user = mysqli_real_escape_string($_SESSION['id']);
+        mysqli_query($con, "INSERT INTO project (id, permalink, type, title, user, visible) VALUES ((SELECT max(id) +1 FROM project p), concat((SELECT max(id) +1 FROM project p), ''), 'M', 'NULL', $user, 0);");
+        $id = mysqli_fetch_array(mysqli_query($con, "SELECT max(id) AS id FROM project;"))["id"];
+        header("Location: /projects/edit.php?p=" + $id);
+    }
+?>

+ 28 - 0
www-admin/projects/uploadimage.php

@@ -0,0 +1,28 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $lang = "en";
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = $proto . substr($http_host, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401);
+        header("Location: /authentication.php");
+    }
+    else{
+        foreach ($_POST as $key => $value){
+            error_log("{$key} = {$value}");
+        }
+        $name = mysqli_real_escape_string($con, $_GET["name"]);
+        $base64string = $_POST["image"];
+        error_log("FILENAME: $name");
+        error_log("PATH: $doc_root../www/img/projects/DEBUG_$name");
+        error_log("B64: $base64string");
+        file_put_contents("$doc_root../www/img/projects/DEBUG_$name", base64_decode($base64string));
+
+        echo("OK");
+    }
+?>

+ 29 - 0
www-admin/projects/visible.php

@@ -0,0 +1,29 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = substr($server, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401); // Not logged in: Forbidden.
+    }
+    else{
+        $id = intval(mysqli_real_escape_string($con, $_GET["p"]));
+        $visible = intval(mysqli_real_escape_string($con, $_GET["v"]));
+        if (strlen($id) < 1 || ($visible != 1 && $visible != 0)){
+            http_response_code(400); // No or bad ID, visible not 1 or 0: Bad request.
+        }
+        else{
+            if (mysqli_num_rows(mysqli_query($con, "SELECT id FROM project WHERE id = $id;")) == 0){
+                http_response_code(404); // Unexistent id: Not found.
+            }
+            else{
+                mysqli_query($con, "UPDATE project SET visible = $visible WHERE id = $id;");
+                http_response_code(200); //OK.
+            }
+        }
+    }
+?>

+ 74 - 0
www-admin/script/blog.js

@@ -0,0 +1,74 @@
+function managePost(id){
+    window.location.href = "/blog/edit.php?p=" + id;
+}
+
+function deletePost(id, title){
+    dialog("confirm", "Delete post", "Do you really want to delete the post '" + title + "'?<br/><br/>There is no way to undo this action. You can also unpublish the post instead of deleting it.", function (confirmed) {
+        if (confirmed) {
+            var xmlhttp;
+            if (window.XMLHttpRequest){
+                xmlhttp = new XMLHttpRequest();
+            }
+            xmlhttp.onreadystatechange=function(){
+                if (xmlhttp.readyState == 4){
+                    if (xmlhttp.status == 200){
+                        location.reload();
+                    }
+                    else{
+                        dialog("alert", "ERROR", "Error deleting the post '" + title + "'.", null);
+                    }
+                }
+            }
+            xmlhttp.open("GET","/blog/delete.php?p=" + id, true);
+            xmlhttp.send();
+        }
+    });
+}
+
+
+function deleteComment(id){
+    dialog("confirm", "Delete version", "Do you really want to delete this comment?<br/><br/>There is no way to undo this action. You can also disapprove the comment instead of deleting it.", function (confirmed) {
+        if (confirmed) {
+            var xmlhttp;
+            if (window.XMLHttpRequest){
+                xmlhttp = new XMLHttpRequest();
+            }
+            xmlhttp.onreadystatechange=function(){
+                if (xmlhttp.readyState == 4){
+                    if (xmlhttp.status == 200){
+                        location.reload();
+                    }
+                    else{
+                        dialog("alert", "ERROR", "Error deleting the comment.", null);
+                    }
+                }
+            }
+            xmlhttp.open("GET","/blog/deletecomment.php?i=" + id, true);
+            xmlhttp.send();
+        }
+    });
+}
+
+function visiblePost(id, title, visible){
+    var xmlhttp;
+    if (window.XMLHttpRequest){
+        xmlhttp = new XMLHttpRequest();
+    }
+    xmlhttp.onreadystatechange=function(){
+        if (xmlhttp.readyState == 4){
+            if (xmlhttp.status == 200){
+                location.reload();
+            }
+            else{
+                dialog("alert", "ERROR", "Error changing visibility of the post '" + title + "'.", null);
+            }
+        }
+    }
+    xmlhttp.open("GET","/post/visible.php?p=" + id + "&v=" + visible, true);
+    xmlhttp.send();
+}
+
+
+function addImage(){
+    
+}

+ 136 - 0
www-admin/script/projects.js

@@ -0,0 +1,136 @@
+function manageProject(id){
+    window.location.href = "/projects/edit.php?p=" + id;
+}
+
+function deleteProject(id, title){
+    dialog("confirm", "Delete project", "Do you really want to delete the project '" + title + "'?<br/><br/>There is no way to undo this action. You can also unpublish the project instead of deleting it.", function (confirmed) {
+        if (confirmed) {
+            var xmlhttp;
+            if (window.XMLHttpRequest){
+                xmlhttp = new XMLHttpRequest();
+            }
+            xmlhttp.onreadystatechange=function(){
+                if (xmlhttp.readyState == 4){
+                    if (xmlhttp.status == 200){
+                        location.reload();
+                    }
+                    else{
+                        dialog("alert", "ERROR", "Error deleting the project '" + title + "'.", null);
+                    }
+                }
+            }
+            xmlhttp.open("GET","/projects/delete.php?p=" + id, true);
+            xmlhttp.send();
+        }
+    });
+}
+
+function deleteVersion(id, title){
+    dialog("confirm", "Delete version", "Do you really want to delete the version '" + title + "'?<br/><br/>There is no way to undo this action. You can also unpublish the version instead of deleting it.", function (confirmed) {
+        if (confirmed) {
+            var xmlhttp;
+            if (window.XMLHttpRequest){
+                xmlhttp = new XMLHttpRequest();
+            }
+            xmlhttp.onreadystatechange=function(){
+                if (xmlhttp.readyState == 4){
+                    if (xmlhttp.status == 200){
+                        location.reload();
+                    }
+                    else{
+                        dialog("alert", "ERROR", "Error deleting the version '" + title + "'.", null);
+                    }
+                }
+            }
+            xmlhttp.open("GET","/projects/deleteversion.php?v=" + id, true);
+            xmlhttp.send();
+        }
+    });
+}
+
+function deleteComment(id){
+    dialog("confirm", "Delete version", "Do you really want to delete this comment?<br/><br/>There is no way to undo this action. You can also disapprove the comment instead of deleting it.", function (confirmed) {
+        if (confirmed) {
+            var xmlhttp;
+            if (window.XMLHttpRequest){
+                xmlhttp = new XMLHttpRequest();
+            }
+            xmlhttp.onreadystatechange=function(){
+                if (xmlhttp.readyState == 4){
+                    if (xmlhttp.status == 200){
+                        location.reload();
+                    }
+                    else{
+                        dialog("alert", "ERROR", "Error deleting the comment.", null);
+                    }
+                }
+            }
+            xmlhttp.open("GET","/projects/deletecomment.php?i=" + id, true);
+            xmlhttp.send();
+        }
+    });
+}
+
+function visibleProject(id, title, visible){
+    var xmlhttp;
+    if (window.XMLHttpRequest){
+        xmlhttp = new XMLHttpRequest();
+    }
+    xmlhttp.onreadystatechange=function(){
+        if (xmlhttp.readyState == 4){
+            if (xmlhttp.status == 200){
+                location.reload();
+            }
+            else{
+                dialog("alert", "ERROR", "Error changing visibility of the project '" + title + "'.", null);
+            }
+        }
+    }
+    xmlhttp.open("GET","/projects/visible.php?p=" + id + "&v=" + visible, true);
+    xmlhttp.send();
+}
+
+function selectIcon(){
+    document.getElementById("icon_file").click();
+}
+
+function changeIcon(event, title){
+    console.log("changeIcon 1");
+    var frame = document.getElementById("icon");
+    var file = event.target.files[0];
+    var reader = new FileReader();
+    var httpPost = new XMLHttpRequest();
+    console.log("changeIcon 2");
+    reader.onloadend = function() {
+        console.log("changeIcon LOADEND 1");
+        console.log('RESULT', reader.result);
+        var data = "image=IVV" + reader.result;
+        var path = "/projects/uploadimage.php?name=" + title + "-icon.png";
+        console.log("changeIcon LOADEND 2");
+        httpPost.open("POST", path, true);
+        console.log("changeIcon LOADEND 3");
+        httpPost.setRequestHeader('Content-Type', 'x-www-form-urlencoded');
+        httpPost.setRequestHeader("Content-length", data.length);
+        console.log("changeIcon LOADEND 4");
+        httpPost.send(data);
+        console.log("changeIcon LOADEND 5 - END");
+    }
+    console.log("changeIcon 3");
+    var b64 = reader.readAsDataURL(file);
+    //var b64 = reader.result;
+    console.log("changeIcon 4");
+    httpPost.onreadystatechange = function(err) {
+        console.log("changeIcon REQCHANGE");
+        if (httpPost.readyState == 4 && httpPost.status == 200){
+            console.log("changeIcon REQOK");
+            frame.src = window.URL.createObjectURL(event.target.files[0]);
+            console.log(httpPost.responseText);
+        }
+        else {
+            console.log("changeIcon REQ-ERROR");
+            console.log(err);
+        }
+    };
+    console.log("changeIcon 5 - END");
+    
+}

+ 227 - 0
www-admin/script/stats.js

@@ -0,0 +1,227 @@
+function BarGraph(context) {
+
+    var graph = this;
+    this.values = [];
+    this.labels = [];
+
+    this.setLabels = function(labels){
+        this.labels = labels;
+    }
+
+    this.setValues = function(values){
+        this.values = values;
+    }
+
+    this.draw = function () {
+
+        var values = graph.values;
+        var barW;
+        var barH;
+        var barB = 1; // Border
+        var ratio;
+        var maxH;
+        var max;
+        var i;
+
+        // Canvas dimensions
+        context.canvas.width = graph.width;
+        context.canvas.height = graph.height;
+
+        // Width of each bar
+        barW = graph.width / values.length - graph.margin * 2;
+        maxH = graph.height - 30;
+
+        // Reference height
+        var max = 0;
+        for (i = 0; i < values.length; i ++) {
+            if (values[i] > max) {
+                max = values[i];	
+            }
+        }
+
+        // Loop bars
+        for (i = 0; i < values.length; i ++) {
+
+            // Compare with the max value
+            barH = maxH * values[i] / max;
+            graph.height -= 15;
+
+            // Value
+            context.fillStyle = "#003300";
+            context.font = "bold 12px sans-serif";
+            context.textAlign = "center";
+            context.fillText(parseInt(values[i], 10), i * graph.width / values.length + (graph.width / values.length) / 2, graph.height - barH - 2);
+
+            // Bar background (border)
+            context.fillStyle = "#003300";
+            context.fillRect(graph.margin + i * graph.width / values.length, graph.height - barH, barW, barH);
+
+            // Bar fill
+            context.fillStyle = "#ccffcc";
+            context.fillRect(graph.margin + i * graph.width / values.length + barB, graph.height - barH + barB, barW - barB * 2, barH - barB * 2);
+
+            // Label
+            graph.height += 15;
+            context.fillStyle = "#003300";
+            context.font = "10px sans-serif";
+            context.textAlign = "center";
+            context.fillText(graph.labels[i], i * graph.width / values.length + (graph.width / values.length) / 2, graph.height - 5);
+        }
+    };
+
+  this.width = 450;
+  this.height = 150;
+  this.margin = 1;
+  
+}
+
+
+function HBarGraph(context) {
+
+    var graph = this;
+    this.values = [];
+    this.labels = [];
+
+    this.setLabels = function(labels){
+        this.labels = labels;
+    }
+
+    this.setValues = function(values){
+        this.values = values;
+    }
+
+    this.draw = function () {
+
+        var values = graph.values;
+        var barW;
+        var barH;
+        var barB = 1; // Border
+        var ratio;
+        var maxH;
+        var max;
+        var i;
+
+        // Canvas dimensions
+        context.canvas.width = graph.width;
+        context.canvas.height = graph.height;
+
+        // Height of each bar
+        barH = graph.height / values.length - graph.margin * 2;
+        maxW = graph.width - 200;
+
+        // Reference width
+        var max = 0;
+        for (i = 0; i < values.length; i ++) {
+            if (values[i] > max) {
+                max = values[i];	
+            }
+        }
+
+        // Loop bars
+        for (i = 0; i < values.length; i ++) {
+
+            // Compare with the max value
+            barW = maxW * values[i] / max;
+
+            // Label
+            context.fillStyle = "#003300";
+            context.font = "10px sans-serif";
+            context.textAlign = "right";
+            context.fillText(graph.labels[i], 150, barH / 2 + (graph.margin + i * graph.height / values.length));
+
+            // Bar background (border)
+            context.fillStyle = "#003300";
+            context.fillRect(160, graph.margin + i * graph.height / values.length, barW, barH);
+
+            // Bar fill
+            context.fillStyle = "#ccffcc";
+            context.fillRect(160 + barB, barB + (graph.margin + i * graph.height / values.length), barW - (2 * barB), barH - (2 * barB));
+
+            // Value
+            context.fillStyle = "#003300";
+            context.font = "bold 12px sans-serif";
+            context.textAlign = "left";
+            context.fillText(parseInt(values[i], 10), 170 + barW, barH / 2 + (graph.margin + i * graph.height / values.length));
+
+        }
+    };
+
+  this.width = 450;
+  this.height = 250;
+  this.margin = 1;
+  
+}
+
+
+function PieGraph(context) {
+
+    var graph = this;
+    this.values = [];
+    this.labels = [];
+
+    this.setLabels = function(labels){
+        this.labels = labels;
+    }
+
+    this.setValues = function(values){
+        this.values = values;
+    }
+
+    this.draw = function () {
+
+        var values = graph.values;
+        var barW;
+        var barH;
+        var barB = 1; // Border
+        var ratio;
+        var maxH;
+        var max;
+        var i;
+
+        // Canvas dimensions
+        context.canvas.width = graph.width;
+        context.canvas.height = graph.height;
+
+        // Sum of values
+        var sum = 0;
+        for (i = 0; i < values.length; i ++) {
+            sum = sum + values[i]
+        }
+
+        // Loop arcs
+        var iAngle = (-0.5) * Math.PI;
+        var hLegend = 30;
+        for (i = 0; i < values.length; i ++) {
+
+            // Calculate angle
+            var v = (values[i] * 2  / sum) * Math.PI;
+
+            // Draw arc
+            context.fillStyle = "#00" + (3 * (i + 3)).toString().substr(-1) + (3 * (i + 3)).toString().substr(-1) + "00";
+            context.strokeStyle = "#00" + (3 * (i + 3)).toString().substr(-1) + (3 * (i + 3)).toString().substr(-1) + "00";
+            context.beginPath();
+            context.arc(100, 100, 50, iAngle, iAngle + v);
+            context.lineWidth = 80;
+            context.stroke();
+
+            // Legend
+            var label = graph.labels[i] + " (" + (values[i] * 100 / sum).toFixed(2) + "%)";
+            context.beginPath();
+            context.arc(215, hLegend, 10, 0, 2 * Math.PI);
+            context.fill();
+            context.font = "10px sans-serif";
+            context.textAlign = "left";
+            context.fillText(label, 230, hLegend + 5);
+
+            // Prepare for next
+            iAngle = iAngle + v;
+            hLegend = hLegend + 30;
+
+        }
+    };
+
+  this.width = 380;
+  this.height = 200;
+  this.margin = 1;
+  
+}

+ 204 - 0
www-admin/script/ui.js

@@ -0,0 +1,204 @@
+/**
+ * Shows one of the headers.
+ * 
+ * @param header Header ID.
+ * @param from Element clicked to open the header. It will be highlighted.
+ */
+function showHeader(header, from){
+    if(document.getElementById('header_' + header).style.display == 'block'){
+        document.getElementById('header_' + header).style.display = 'none';
+        from.style.backgroundColor = '#000000';
+    }
+    else{
+        var headers = document.getElementsByClassName('secondary_header');
+        for (var i = 0; i < headers.length; i ++)
+                headers.item(i).style.display = 'none';
+        var sections = document.getElementsByClassName('header_section');
+        for (var j = 0; j < sections.length; j ++)
+                sections.item(j).style.backgroundColor = '#000000';
+        
+        document.getElementById('header_' + header).style.display = 'block';
+        from.style.backgroundColor = '#333333';
+    }
+}
+
+
+/**
+ * Shows a language tab.
+ * 
+ * @param lang Two letter language code. 'es', 'en' or 'eu'.
+ */
+function showLanguage(lang){
+    var en = document.getElementsByClassName('content_lang_en');
+    var es = document.getElementsByClassName('content_lang_es');
+    var eu = document.getElementsByClassName('content_lang_eu');
+    switch (lang){
+        case 'es':
+            for (var i = 0; i < en.length; ++i) {
+                en[i].style.display = 'none';
+                es[i].style.display = 'block';
+                eu[i].style.display = 'none';
+            }
+            document.getElementById('lang_tab_es').classList.add("lang_tabs_active");
+            document.getElementById('lang_tab_eu').classList.remove("lang_tabs_active");
+            document.getElementById('lang_tab_en').classList.remove("lang_tabs_active");
+            break;
+        case 'eu':
+            for (var i = 0; i < en.length; ++i) {
+                en[i].style.display = 'none';
+                es[i].style.display = 'none';
+                eu[i].style.display = 'block';
+            }
+            document.getElementById('lang_tab_es').classList.remove("lang_tabs_active");
+            document.getElementById('lang_tab_eu').classList.add("lang_tabs_active");
+            document.getElementById('lang_tab_en').classList.remove("lang_tabs_active");
+            break;
+        case 'en':
+            for (var i = 0; i < en.length; ++i) {
+                en[i].style.display = 'block';
+                es[i].style.display = 'none';
+                eu[i].style.display = 'none';
+            }
+            document.getElementById('lang_tab_es').classList.remove("lang_tabs_active");
+            document.getElementById('lang_tab_eu').classList.remove("lang_tabs_active");
+            document.getElementById('lang_tab_en').classList.add("lang_tabs_active");
+    }
+}
+
+function dialog(type, title, message, callback) {
+    var dialogWindow = document.getElementById('dialog');
+    var dialogText = document.getElementById('dialog_text');
+    var dialogTitle = document.getElementById('dialog_title');
+    var background = document.getElementById('dialog_background');
+    var ynContainer = document.getElementById('dialog_button_yn_container');
+    var aContainer = document.getElementById('dialog_button_a_container');
+    var yButton = document.getElementById('dialog_yes');
+    var nButton = document.getElementById('dialog_no');
+    var aButton = document.getElementById('dialog_accept');
+
+    function show(){
+        dialogTitle.innerText = title;
+        dialogText.innerHTML = message;
+        dialogWindow.style.display = 'block';
+        dialogWindow.style.opacity = '1';
+    };
+    function hide(){
+        dialogWindow.style.display = 'none';
+        dialogWindow.style.opacity = '0';
+    };
+
+    // Set up the dialog type
+    if (type == "confirm"){
+        ynContainer.style.display = "block";
+        aContainer.style.display = "none";
+        yButton.onclick = function() {
+            hide();
+            callback(true);
+        }
+        nButton.onclick = function() { 
+            hide();
+            callback(false);
+        }
+    }
+    else if (type == "alert"){
+        ynContainer.style.display = "none";
+        aContainer.style.display = "block";
+        aButton.onclick = function() {
+            hide();
+            callback(true);
+        };
+    }
+    show();
+}
+
+
+function enableEdit(button){
+    var container = button.parentElement;
+    var fieldResult = container.getElementsByClassName('field_result')[0];
+    var fieldEditable = container.getElementsByClassName('field_editable')[0];
+    var cke = container.getElementsByClassName('cke')[0];
+    var buttonEdit = container.getElementsByClassName('button_edit')[0];
+    var buttonSave = container.getElementsByClassName('button_save')[0];
+    var buttonCancel = container.getElementsByClassName('button_cancel')[0];
+    fieldEditable.value = fieldResult.innerHTML;
+    fieldResult.style.display = 'none';
+    if (typeof(cke) != 'undefined' && cke != null){
+        cke.style.display = 'block';
+    }
+    else{
+        fieldEditable.style.display = 'initial';
+    }
+    buttonEdit.style.display = 'none';
+    buttonSave.style.display = 'initial';
+    buttonCancel.style.display = 'initial';
+}
+
+function saveEdit(button, table, column, id){
+    var container = button.parentElement;
+    var fieldResult = container.getElementsByClassName('field_result')[0];
+    var fieldEditable = container.getElementsByClassName('field_editable')[0];
+    var cke = container.getElementsByClassName('cke')[0];
+    var buttonEdit = container.getElementsByClassName('button_edit')[0];
+    var buttonSave = container.getElementsByClassName('button_save')[0];
+    var buttonCancel = container.getElementsByClassName('button_cancel')[0];
+    var value;
+    if (typeof(cke) != 'undefined' && cke != null){
+        fieldEditable.innerHTML = CKEDITOR.instances['text_en'].getData()
+        fieldResult.innerHTML = fieldEditable.innerHTML;
+        value = escape(fieldEditable.innerHTML);
+    }
+    else{
+        value = escape(fieldEditable.value);
+    }
+    // TODO: Do some validation.
+    
+    var xmlhttp;
+            if (window.XMLHttpRequest){
+                xmlhttp = new XMLHttpRequest();
+            }
+            xmlhttp.onreadystatechange=function(){
+                if (xmlhttp.readyState == 4){
+                    if (xmlhttp.status == 200){
+                        if (typeof(cke) != 'undefined' && cke != null){
+                            cke.style.display = 'none';
+                            fieldEditable.innerHTML = CKEDITOR.instances['text_en'].getData()
+                            fieldResult.innerHTML = fieldEditable.innerHTML;
+                        }
+                        else{
+                            fieldEditable.style.display = 'none';
+                            fieldResult.innerHTML = fieldEditable.value;
+                        }
+                        fieldResult.style.display = 'initial';
+                        buttonEdit.style.display = 'initial';
+                        buttonSave.style.display = 'none';
+                        buttonCancel.style.display = 'none';
+                    }
+                    else{
+                        dialog("alert", "ERROR", "Error saving changes.", null);
+                    }
+                }
+            }
+            xmlhttp.open("GET","/functions/save.php?t=" + table + "&c=" + column + "&v=" + value + "&i=" + id, true);
+            xmlhttp.send();
+}
+
+function cancelEdit(button){
+    var container = button.parentElement;
+    var fieldResult = container.getElementsByClassName('field_result')[0];
+    var fieldEditable = container.getElementsByClassName('field_editable')[0];
+    var cke = container.getElementsByClassName('cke')[0];
+    var buttonEdit = container.getElementsByClassName('button_edit')[0];
+    var buttonSave = container.getElementsByClassName('button_save')[0];
+    var buttonCancel = container.getElementsByClassName('button_cancel')[0];
+    fieldEditable.value = fieldResult.innerHTML;
+    fieldResult.style.display = 'initial';
+        if (typeof(cke) != 'undefined' && cke != null){
+        cke.style.display = 'none';
+    }
+    else{
+        fieldEditable.style.display = 'none';
+    }
+    buttonEdit.style.display = 'initial';
+    buttonSave.style.display = 'none';
+    buttonCancel.style.display = 'none';
+}

+ 0 - 0
www-admin/settings/index.php


+ 0 - 0
www-admin/settings/profile.php


+ 269 - 0
www-admin/stats/index.php

@@ -0,0 +1,269 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"] . "/";
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $lang = "en";
+    $con = start_db();
+    $server = $proto . $http_host;
+    $pserver = $proto . substr($http_host, 0, strpos($http_host, ':')); // public server
+    if (!check_session($con)){
+        http_response_code(401);
+        header("Location: /authentication.php");
+    }
+    else{
+?>
+<html>
+    <head>
+        <meta content='text/html; charset=windows-1252' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'>
+        <link rel='shortcut icon' href='<?=$pserver?>/img/logo/x2/favicon.ico'/>
+        <title><?=$title?>Stats - I&ntilde;igo Valentin - Administration Panel</title>
+        <style>
+<?php
+            include $doc_root . "css/ui.css";
+            include $doc_root . "css/stats.css";
+?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media='(max-width : 990px)'>
+<?php
+            include $doc_root . "css/m/ui.css";
+            include $doc_root . "css/m/stats.css";
+?>
+        </style>
+        <!-- Script files -->
+        <script type='text/javascript'>
+<?php
+            include $doc_root . "script/ui.js";
+            include $doc_root . "script/stats.js";
+?>
+        </script>
+    </head>
+    <body>
+<?php
+        include $doc_root . "header.php";
+?>
+        <div id='content'>
+            <div class='section' id='post_edit'>
+                <h3 class='section_title'>Visits</h3>
+                <div class='entry'>
+                    <div id='visitCanvas'></div>
+                    <script>
+                        (function () {
+                            function createVisitCanvas() {
+                                var parent = document.getElementById("visitCanvas");
+                                var canvas = document.createElement("canvas");
+                                parent.appendChild(canvas);
+                                return canvas.getContext("2d");
+                            }
+                            var context = createVisitCanvas();
+                            var graph = new BarGraph(context);
+<?php
+                            $q_month = mysqli_query($con, "SELECT month(dtime) AS month, count(id) AS total FROM stat_visit GROUP BY year(dtime), month(dtime) ORDER BY dtime DESC LIMIT 12;");
+                            $name_string = "";
+                            $valu_string = "";
+                            $month = date('m');
+
+                            // Show at least the last twelve months, even if there is no data that old.
+                            for ($i = 0; $i < 12 - mysqli_num_rows($q_month); $i ++){
+                                $month_number = intval($month) - (12 - $i) + 1;
+                                if ($month_number <= 0){
+                                    $month_number = $month_number + 12;
+                                }
+                                $name_string = $name_string . "\"" . ucfirst(substr(text($con, "MONTH_" . str_pad($month_number, 2, "0", STR_PAD_LEFT), $lang), 0, 3)) . "\", ";     //"\"" . text($con, "MONTH_" . $r_month["month"], $lang) . "\", ";
+                                $valu_string = $valu_string . "0, ";
+                            }
+
+                            // Loop data
+                            while ($r_month = mysqli_fetch_array($q_month)){
+                                $name_string = $name_string . "\"" . ucfirst(substr(text($con, "MONTH_" . $r_month["month"], $lang), 0, 3)) . "\", ";
+                                $valu_string = $valu_string . $r_month["total"] . ", ";
+                            }
+                            $name_string = substr(trim($name_string), 0, -1);
+                            $valu_string = substr(trim($valu_string), 0, -1);
+?>
+                            graph.width = parseInt(window.getComputedStyle(document.getElementById("visitCanvas")).width);
+                            graph.height = 180;
+                            graph.setLabels([<?=$name_string?>]);
+                            graph.setValues([<?=$valu_string?>]);
+                            graph.draw();
+                        }());
+                    </script>
+                </div> <!-- .entry -->
+            </div> <!-- .section -->
+            <div class='section' id='post_edit'>
+                <h3 class='section_title'>Most popular pages</h3>
+                <div class='entry'>
+                    <div id='pagesCanvas'></div>
+                    <script>
+                        (function () {
+                            function createPagesCanvas() {
+                                var parent = document.getElementById("pagesCanvas");
+                                var canvas = document.createElement("canvas");
+                                parent.appendChild(canvas);
+                                return canvas.getContext("2d");
+                            }
+                            var context = createPagesCanvas();
+                            var graph = new HBarGraph(context);
+                            
+<?php
+                            $q_visit = mysqli_query($con, "SELECT count(id) AS total, section, entry FROM stat_view GROUP BY section, entry LIMIT 10;");
+                            $name_string = "";
+                            $valu_string = "";
+                            while ($r_visit = mysqli_fetch_array($q_visit)){
+                                $s = $r_visit["section"];
+                                $e = $r_visit["entry"];
+                                switch ($s){
+                                    case "index":
+                                        $name_string = $name_string . "\"Homepage\", ";
+                                        break;
+                                    case "profile":
+                                        $name_string = $name_string . "\"Profile page\", ";
+                                        break;
+                                    case "help":
+                                        $name_string = $name_string . "\"Help page\", ";
+                                        break;
+                                    case "project":
+                                        if (strlen(e) == 0){
+                                            $name_string = $name_string . "\"Projects page\", ";
+                                        }
+                                        else{
+                                            $t = text($con, mysqli_fetch_array(mysqli_query($con, "SELECT title, permalink FROM project WHERE id = $e;"))["title"], $lang);
+                                            $name_string = $name_string . "\"Project: " . $t . "\", ";
+                                        }
+                                        break;
+                                    case "blog":
+                                        if (strlen(e) == 0){
+                                            $name_string = $name_string . "\"Blog\", ";
+                                        }
+                                        else{
+                                            $t = text($con, mysqli_fetch_array(mysqli_query($con, "SELECT title, permalink FROM project WHERE id = $e;"))["title"], $lang);
+                                            $name_string = $name_string . "\"Post: " . $t . "\", ";
+                                        }
+                                        break;
+                                    default:
+                                        $name_string = $name_string . "\"" . $s . " - " . $e . "\", ";
+                                }
+                                $valu_string = $valu_string . $r_visit["total"] . ", ";
+                            }
+                            $name_string = substr(trim($name_string), 0, -1);
+                            $valu_string = substr(trim($valu_string), 0, -1);
+?>
+                            setTimeout(function(){
+                                graph.width = parseInt(window.getComputedStyle(document.getElementById("pagesCanvas")).width);
+                                graph.height = <?=(40 + (30 * mysqli_num_rows($q_visit)))?>;
+                                graph.setLabels([<?=$name_string?>]);
+                                graph.setValues([<?=$valu_string?>]);
+                                graph.draw();
+                            }, 0);
+                        }());
+                    </script>
+                </div> <!-- .entry -->
+            </div> <!-- .section -->
+            <div class='section'>
+                <h3 class='section_title'>Visitors profile</h3>
+                <div class='entry'>
+                    <div class='pie_chart' id='osCanvas'><h4>Top OS</h4></div>
+                    <div class='pie_chart' id='browserCanvas'><h4>Top browsers</h4></div>
+                    <div class='pie_chart' id='osBrowserCanvas'><h4>Top OS/browser</h4></div>
+                    <script>
+                        (function () {
+                            function createOsCanvas() {
+                                var parent = document.getElementById("osCanvas");
+                                var canvas = document.createElement("canvas");
+                                parent.appendChild(canvas);
+                                return canvas.getContext("2d");
+                            }
+                            var context = createOsCanvas();
+                            var graph = new PieGraph(context);
+<?php
+                            $q_os = mysqli_query($con, "SELECT count(id) AS total, os FROM stat_visit GROUP BY os ORDER BY total DESC LIMIT 9;");
+                            $name_string = "";
+                            $valu_string = "";
+                            while ($r_os = mysqli_fetch_array($q_os)){
+                                $name_string = $name_string . "\"" . $r_os["os"] . "\", ";
+                                $valu_string = $valu_string . $r_os["total"] . ", ";
+                            }
+                            $name_string = substr(trim($name_string), 0, -1);
+                            $valu_string = substr(trim($valu_string), 0, -1);
+?>
+                            var cHeight = 30 * (<?=mysqli_num_rows($q_os)?> + 3);
+                            if (cHeight < 200){
+                                cHeight = 200;
+                            }
+                            graph.width = 400;
+                            graph.height = cHeight;
+                            graph.setLabels([<?=$name_string?>]);
+                            graph.setValues([<?=$valu_string?>]);
+                            graph.draw();
+                        }());
+                        (function () {
+                            function createBrowserCanvas() {
+                                var parent = document.getElementById("browserCanvas");
+                                var canvas = document.createElement("canvas");
+                                parent.appendChild(canvas);
+                                return canvas.getContext("2d");
+                            }
+                            var context = createBrowserCanvas();
+                            var graph = new PieGraph(context);
+<?php
+                            $q_browser = mysqli_query($con, "SELECT count(id) AS total, browser FROM stat_visit GROUP BY browser ORDER BY total DESC LIMIT 9;");
+                            $name_string = "";
+                            $valu_string = "";
+                            while ($r_browser = mysqli_fetch_array($q_browser)){
+                                $name_string = $name_string . "\"" . $r_browser["browser"] . "\", ";
+                                $valu_string = $valu_string . $r_browser["total"] . ", ";
+                            }
+                            $name_string = substr(trim($name_string), 0, -1);
+                            $valu_string = substr(trim($valu_string), 0, -1);
+?>
+                            var cHeight = 30 * (<?=mysqli_num_rows($q_browser)?> + 3);
+                            if (cHeight < 200){
+                                cHeight = 200;
+                            }
+                            graph.width = 400;
+                            graph.height = cHeight;
+                            graph.setLabels([<?=$name_string?>]);
+                            graph.setValues([<?=$valu_string?>]);
+                            graph.draw();
+                        }());
+                        (function () {
+                            function createOsBrowserCanvas() {
+                                var parent = document.getElementById("osBrowserCanvas");
+                                var canvas = document.createElement("canvas");
+                                parent.appendChild(canvas);
+                                return canvas.getContext("2d");
+                            }
+                            var context = createOsBrowserCanvas();
+                            var graph = new PieGraph(context);
+<?php
+                            $q_osbrowser = mysqli_query($con, "SELECT count(id) AS total, os, browser FROM stat_visit GROUP BY os, browser ORDER BY total DESC LIMIT 9;");
+                            $name_string = "";
+                            $valu_string = "";
+                            while ($r_osbrowser = mysqli_fetch_array($q_osbrowser)){
+                                $name_string = $name_string . "\"" . $r_osbrowser["os"] . "/" . $r_osbrowser["browser"] . "\", ";
+                                $valu_string = $valu_string . $r_osbrowser["total"] . ", ";
+                            }
+                            $name_string = substr(trim($name_string), 0, -1);
+                            $valu_string = substr(trim($valu_string), 0, -1);
+?>
+                            var cHeight = 30 * (<?=mysqli_num_rows($q_osbrowser)?> + 3);
+                            if (cHeight < 200){
+                                cHeight = 200;
+                            }
+                            graph.width = 400;
+                            graph.height = cHeight;
+                            graph.setLabels([<?=$name_string?>]);
+                            graph.setValues([<?=$valu_string?>]);
+                            graph.draw();
+                        }());
+                    </script>
+                </div> <!-- .entry -->
+            </div> <!-- .section -->
+        </div>
+    </div>
+<?php
+    }
+?>

+ 125 - 0
www/.htaccess

@@ -0,0 +1,125 @@
+Options -MultiViews
+
+
+
+#
+# REWRITE
+#
+# Used to: create nice urls, force https, and hav the site multilanguage.
+#
+<IfModule mod_rewrite.c>
+	RewriteEngine On
+	RewriteBase /
+
+	# Force https
+	RewriteCond %{HTTPS} !=on
+	RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
+
+	# Add final slash.
+	RewriteRule ^([a-zA-Z]{2})$ $1/ [R=301]
+
+	# For files, remove the last /
+	RewriteCond %{REQUEST_URI} ^/([a-zA-Z]{2}/)?(.{3,})/$
+	RewriteCond %{DOCUMENT_ROOT}/%2 !-d
+	RewriteCond %{REQUEST_URI} /$
+	RewriteRule ^(.{3,})/$ $1 [R=301]
+
+	# For sections, add the last /
+	RewriteCond %{REQUEST_URI} ^/([a-zA-Z]{2}/)?(.{3,})$
+	RewriteCond %{DOCUMENT_ROOT}/%2 -d
+	RewriteCond %{REQUEST_URI} !/$
+	RewriteRule ^(.{3,})$ $1/ [R=301]
+
+	# Hide the 'index.php'
+	RewriteRule ^(.*)index\.php?$ $1 [L,R=301]
+
+	# Remove page extensions
+	RewriteCond %{THE_REQUEST} ^GET\ (.*)\.(php|html)([#?][^\ ]*)?\ HTTP
+	RewriteRule ^(.*)\.(php|html)$ $1 [R=301]
+
+	# Redirect permalinks.
+        RewriteRule ^project/([A-Z\-a-z0-9]{3,}+)[\/]?$ /project/project.php?permalink=$1 [NC]
+        RewriteRule ^blog/([A-Z\-a-z0-9]{3,}+)[\/]?$ /blog/post.php?permalink=$1 [NC]
+        RewriteRule ^help[\/]?$ /help/index.php [NC]
+        RewriteRule ^(perfil|xprofile|profila)[\/]?$ /profile/index.php [NC]
+
+        # PHP to xml.
+        RewriteRule ^sitemap.xml$ /sitemap.php [NC]
+        RewriteRule ^imgmap.xml$ /sitemap-image.php [NC]
+        RewriteRule ^rss.xml$ /rss.php
+
+	# Language as GET param.
+	RewriteCond %{REQUEST_FILENAME} !-d
+	RewriteCond %{REQUEST_FILENAME} !-f
+	RewriteCond %{REQUEST_URI} ^/([a-zA-Z]{2})(/(.*))?$
+	RewriteRule ^([a-zA-Z]{2})(/(.*))?$ $3?lang=$1 [L,QSA]
+
+	# Add .php extension when needed. TODO: Actualy used?
+	RewriteCond %{REQUEST_FILENAME} !-d
+	RewriteCond %{REQUEST_FILENAME} !-f
+	RewriteCond %{REQUEST_FILENAME}.php -f
+	RewriteRule ^(.*)$ $1\.php [L]
+
+</ifModule>
+
+
+#
+# EXPIRES
+#
+# Used to: control the cache times of the different files on the server.
+#
+<IfModule mod_expires.c>
+        ExpiresActive on
+	ExpiresDefault "access plus 2 days"
+        ExpiresByType application/javascript "access plus 6 days"
+        ExpiresByType image/x-ico "access plus 12 days"
+        ExpiresByType image/jpg "access plus 12 days"
+        ExpiresByType image/jpeg "access plus 12 days"
+        ExpiresByType image/gif "access plus 12 days"
+        ExpiresByType image/png "access plus 12 days"
+        ExpiresByType video/mkv "access plus 12 days"
+        #ExpiresByType text/css "access plus 6 days"
+        ExpiresByType text/plain "access plus 6 days"
+</IfModule>
+
+
+#
+# HEADERS
+#
+# Used to: control the cache times of the different files on the client.
+#
+<ifModule mod_headers.c>
+	<filesMatch "\.(ico|jpe?g|png|gif|swf)$">
+		Header set Cache-Control "max-age=864000, public, must-revalidate"
+	</filesMatch>
+#	<filesMatch "\.(css)$">
+#		Header set Cache-Control "max-age=864000, public, must-revalidate"
+#	</filesMatch>
+	<filesMatch "\.(js)$">
+		Header set Cache-Control "max-age=864000, private, must-revalidate"
+	</filesMatch>
+	<filesMatch "\.(x?html?|php)$">
+		Header set Cache-Control "max-age=86400, private, must-revalidate"
+	</filesMatch>
+</ifModule>
+
+<ifModule mod_deflate.c>
+	AddOutputFilterByType DEFLATE text/html text/xml text/css text/plain
+	AddOutputFilterByType DEFLATE image/x-icon
+	AddOutputFilterByType DEFLATE image/svg+xml application/xhtml+xml application/xml
+	AddOutputFilterByType DEFLATE application/rdf+xml application/rss+xml application/atom+xml
+	AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript application/json
+	AddOutputFilterByType DEFLATE application/x-font-ttf application/x-font-otf
+	AddOutputFilterByType DEFLATE font/truetype font/opentype
+</ifModule>
+
+#
+# ERROR
+#
+# Used to: redirect to nice error pages.
+#
+ErrorDocument 403 /error/403.php
+ErrorDocument 404 /error/404.php
+ErrorDocument 500 /error/500.php
+
+

+ 152 - 20
www/blog/index.php

@@ -1,14 +1,16 @@
 <?php
     session_start();
     $http_host = $_SERVER["HTTP_HOST"];
-    $doc_root = $_SERVER["DOCUMENT_ROOT"]; 
+    $doc_root = $_SERVER["DOCUMENT_ROOT"];
     include $doc_root . "functions.php";
     $proto = get_protocol();
     $con = start_db();
     $server = $proto . $http_host;
     $lang = select_language();
-    $cur_section = "";
+    $lserver = $server . "/" . $lang;
+    $cur_section = "blog";
     $cur_entry = "";
+
 ?>
 <!DOCTYPE html>
 <html>
@@ -16,45 +18,46 @@
         <meta content='text/html; charset=utf-8' http-equiv='content-type'/>
         <meta charset='utf-8'/>
         <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
-        <title>I&ntilde;igo Valentin</title>
-        <link rel='shortcut icon' href='<?=$server?>/img/logo/favicon.ico'/>
+        <title><?=text($con, "BLOG_TITLE", $lang)?> - I&ntilde;igo Valentin</title>
+        <link rel='shortcut icon' href='<?=$lserver?>/img/logo/favicon.ico'/>
         <!-- CSS files -->
         <style>
 <?php
             include $doc_root . "css/ui.css";
-            include $doc_root . "css/home.css";
+            include $doc_root . "css/blog.css";
 ?>
         </style>
         <!-- CSS for mobile version -->
         <style media='(max-width : 990px)'>
 <?php
             include $doc_root . "css/m/ui.css";
-            include $doc_root . "css/m/home.css";
+            include $doc_root . "css/m/blog.css";
 ?>
         </style>
         <!-- Script files -->
         <script type='text/javascript'>
 <?php
             include $doc_root . "script/ui.js";
+            include $doc_root . "script/blog.js";
 ?>
         </script>
         <!-- Meta tags -->
-        <link rel='canonical' href='<?=$server?>'/>
-        <link rel='author' href='<?=$server?>'/>
-        <link rel='publisher' href='<?=$server?>'/>
-        <meta name='description' content=''/>
-        <meta property='og:title' content='I&ntilde;igo Valentin'/>
-        <meta property='og:url' content='<?=$server?>'/>
-        <meta property='og:description' content=''/>
-        <meta property='og:image' content=''/>
+        <link rel='canonical' href='<?=$lserver?>/blog/'/>
+        <link rel='author' href='<?=$lserver?>'/>
+        <link rel='publisher' href='<?=$lserver?>'/>
+        <meta name='description' content='<?=text($con, "BLOG_DESCRIPTION", $lang)?>'/>
+        <meta property='og:title' content='<?=text($con, "BLOG_TITLE", $lang)?> - I&ntilde;igo Valentin'/>
+        <meta property='og:url' content='<?=$lserver?>/blog/'/>
+        <meta property='og:description' content='<?=text($con, "BLOG_DESCRIPTION", $lang)?>'/>
+        <meta property='og:image' content='<?=$lserver?>/img/logo/favicon.ico'/>
         <meta property='og:site_name' content='I&ntilde;igo Valentin'/>
         <meta property='og:type' content='website'/>
         <meta property='og:locale' content='<?=$lang?>'/>
         <meta name='twitter:card' content='summary'/>
-        <meta name='twitter:title' content='I&ntilde;igo Valentin'/>
-        <meta name='twitter:description' content=''/>
-        <meta name='twitter:image' content=''/>
-        <meta name='twitter:url' content='<?=$server?>'/>
+        <meta name='twitter:title' content='<?=text($con, "BLOG_TITLE", $lang)?> - I&ntilde;igo Valentin'/>
+        <meta name='twitter:description' content='<?=text($con, "BLOG_DESCRIPTION", $lang)?>'/>
+        <meta name='twitter:image' content='<?=$lserver?>/img/logo/favicon.ico'/>
+        <meta name='twitter:url' content='<?=$lserver?>/project/'/>
         <meta name='robots' content='index follow'/>
     </head>
     <body>
@@ -62,8 +65,137 @@
         include $doc_root . "header.php";
 ?>
         <div id='content'>
-
-        </div> <!-- #content -->
+            <div id='content_row'>
+                <div class='content_cell' id='content_cell_main'>
+                    <div id='main_column' class='section'>
+                        <h3 class='section_title'><?=text($con, "BLOG_TITLE", $lang)?></h3>
+<?php
+                            $q_blog = mysqli_query($con, "SELECT id, permalink, title, text, DATE_FORMAT(dtime, '%Y-%m-%dT%T') AS isodate, dtime, comments FROM post WHERE visible = 1 ORDER BY dtime DESC;");
+                            while($r_blog = mysqli_fetch_array($q_blog)){
+?>
+                                <div itemscope itemtype='http://schema.org/BlogPosting' class='entry entry_list blog_entry'>
+                                    <meta itemprop='inLanguage' content='<?=$lang?>'/>
+                                    <meta itemprop='datePublished dateModified' content='<?=$r_blog["isodate"]?>'/>
+                                    <meta itemprop='headline name' content='<?=text($con, $r_blog["title"], $lang)?>'/>
+                                    <meta itemprop='articleBody text' content='<?=text($con, $r_blog["text"], $lang)?>'/>
+                                    <meta itemprop='mainEntityOfPage' content='<?=$lserver?>'/>
+                                    <h3 class='entry_title'>
+                                        <a itemprop='url' href='<?=$lserver?>/blog/<?=$r_blog["permalink"]?>'><?=$r_blog["title"]?></a>
+                                    </h3>
+<?php
+                                    $q_image = mysqli_query($con, "SELECT image FROM post_image WHERE post = $r_blog[id] ORDER BY idx LIMIT 1;");
+                                    if (mysqli_num_rows($q_image) > 0){
+                                        $r_image = mysqli_fetch_array($q_image);
+?>
+                                        <div class='post_list_image_container'>
+                                            <a href='<?=$lserver?>/blog/<?=$r_blog["permalink"]?>'>
+                                                <meta itemprop='image' content='<?=$lserver?>/img/blog/view/<?=$r_image["image"]?>'/>
+                                                <img class='post_list_image alt='<?=$r_blog["title"]?>' src='<?=$lserver?>/img/blog/miniature/<?=$r_image["image"]?>'/>
+                                            </a>
+                                        </div>
+<?php
+                                    }
+?>
+                                    <p><?=cutText($r_blog["text"], 800, text($con, "BLOG_MORE", $lang), "$lserver/blog/$r_blog[permalink]/")?></p>
+                                    <hr/>
+                                    <table class='post_footer'>
+                                        <tr>
+<?php
+                                            // Tags (if any), date, and comment counter
+                                            $q_tag = mysqli_query($con, "SELECT tag FROM post_tag WHERE post= $r_blog[id];");
+                                            if (mysqli_num_rows($q_tag) > 0){
+                                                $tag_string = "<span class='tags desktop'>" . text($con, "BLOG_TAGS", $lang);
+                                                $tag_raw = "";
+                                                while ($r_tag = mysqli_fetch_array($q_tag)){
+                                                    $tag_string = $tag_string . "<a href='$proto$http_host/blog/search/tag/$r_tag[tag]'> " . text($con, $r_tag["tag"], $lang) . "</a>, ";
+                                                    $tag_raw = $tag_raw . "$r_tag[tag],";
+                                                }
+                                                $tag_string = substr($tag_string, 0, strlen($tag_string) - 2);
+                                                $tag_string = $tag_string . "</span>";
+                                                $tag_raw = substr($tag_raw, 0, strlen($tag_raw) - 1);
+?>
+                                                <meta itemprop='keywords' content='<?=$tag_raw?>'/>
+                                                <td>
+                                                    <span class='date'><?=format_date($r_blog["dtime"], $lang, false)?></span>
+                                                    &nbsp;&nbsp;&nbsp;<?=$tag_string?>
+                                                </td>
+<?php
+                                            }
+                                            else{
+?>
+                                                <td>
+                                                    <span class='date'><?=format_date($r_blog['dtime'], $lang, false)?></span>
+                                                </td>
+<?php
+                                            }
+                                            #Comment counter
+                                            if ($r_blog["comments"] == 1){
+                                                $q_comment = mysqli_query($con, "SELECT count(id) AS count FROM post_comment WHERE post = $r_blog[id] AND visible = 1;");
+                                                $r_comment = mysqli_fetch_array($q_comment);
+?>
+                                                <meta itemprop='commentCount interationCount' content='<?=$r_comment["count"]?>'/>
+                                                <td class='r_comment'>
+<?php
+                                                    if ($r_comment['count'] == 1){
+?>
+                                                        <span class='comment_counter'>1 <?=text($con, "BLOG_COMMENTS_1", $lang)?></span>
+<?php
+                                                    }
+                                                    else if ($r_comment['count'] == 0){
+?>
+                                                        <span class='comment_counter'><?=text($con, "BLOG_COMMENTS_0", $lang)?></span>
+<?php
+                                                    }
+                                                    else{
+?>
+                                                        <span class='comment_counter'>$<?=r_comment["count"]?> <?=text($con, "BLOG_COMMENTS_X", $lang)?></span>
+<?php
+                                                    }
+?>
+                                                </td>
+<?php
+                                            } // if ($r_blog["comments"] == 1)
+?>
+                                        </tr>
+                                    </table> <!-- .post_footer -->
+                                </div> <!-- .blog_entry -->
+                                </br>
+<?php
+                            } // while($r_blog = mysqli_fetch_array($q_blog))
+?>
+                    </div> <!-- #main_column -->
+                </div> <!-- #content_cell_main -->
+                <div class='content_cell'  id='content_cell_right'>
+                    <div class='section section_no_title' id='right_column'>
+                        <div class='entry'>
+                            <h4><?=text($con, "BLOG_SEARCH", $lang)?></h4>
+                            <div id='search_panel_inputs'>
+                                <form action='#' onSubmit='event.preventDefault();launchSearch("<?=$lng["search_field_all"]?>");'>
+                                    <input id='search_panel_input' class='search_element' type='text' name='query' placeholder='<?=text($con, "BLOG_SEARCH", $lang)?>'/>
+                                    <input id='search_panel_submit' class='search_element' type='submit' value='<?=text($con, "BLOG_SEARCH", $lang)?>'/>
+                                </form>
+                            </div>
+                        </div> <!-- .entry -->
+                        <div class='entry'>
+                            <h4><?=text($con, "BLOG_TAG_CLOUD", $lang)?></h4>
+<?php
+                            //Get the most used tag
+                            $q_max = mysqli_query($con, "SELECT max(count) AS max FROM (SELECT count(post) AS count FROM post_tag GROUP BY tag) AS t;");
+                            $r_max = mysqli_fetch_array($q_max);
+                                $max = $r_max["max"];
+                            $q_tag = mysqli_query($con, "SELECT tag, count(post) AS count FROM post_tag GROUP BY tag;");
+                            while($r_tag = mysqli_fetch_array($q_tag)){
+                                $size = round(60 + 100 * $r_tag['count'] / $max);
+?>
+                                <a href='<?=$lserver?>/blog/search/tag/<?=$r_tag["tag"]?>'>
+                                    <span style='font-size: <?=$size?>%'><?=text($con, $r_tag["tag"], $lang)?></span>
+                                </a>
+<?php
+                            }
+?>
+                        </div> <!-- .entry -->
+                    </div> <!-- #right_column -->
+                </div> <!-- #content -->
 <?php
         include $doc_root . "footer.php";
         stats($con, $cur_section, $cur_entry);

+ 8 - 7
www/blog/post.php

@@ -1,12 +1,13 @@
 <?php
     session_start();
     $http_host = $_SERVER["HTTP_HOST"];
-    $doc_root = $_SERVER["DOCUMENT_ROOT"]; 
+    $doc_root = $_SERVER["DOCUMENT_ROOT"];
     include $doc_root . "functions.php";
     $proto = get_protocol();
     $con = start_db();
     $server = $proto . $http_host;
     $lang = select_language();
+    $lserver = $server . "/" . $lang;
     $cur_section = "";
     $cur_entry = "";
 ?>
@@ -17,7 +18,7 @@
         <meta charset='utf-8'/>
         <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
         <title>I&ntilde;igo Valentin</title>
-        <link rel='shortcut icon' href='<?=$server?>/img/logo/favicon.ico'/>
+        <link rel='shortcut icon' href='<?=$lserver?>/img/logo/favicon.ico'/>
         <!-- CSS files -->
         <style>
 <?php
@@ -39,12 +40,12 @@
 ?>
         </script>
         <!-- Meta tags -->
-        <link rel='canonical' href='<?=$server?>'/>
-        <link rel='author' href='<?=$server?>'/>
-        <link rel='publisher' href='<?=$server?>'/>
+        <link rel='canonical' href='<?=$lserver?>'/>
+        <link rel='author' href='<?=$lserver?>'/>
+        <link rel='publisher' href='<?=$lserver?>'/>
         <meta name='description' content=''/>
         <meta property='og:title' content='I&ntilde;igo Valentin'/>
-        <meta property='og:url' content='<?=$server?>'/>
+        <meta property='og:url' content='<?=$lserver?>'/>
         <meta property='og:description' content=''/>
         <meta property='og:image' content=''/>
         <meta property='og:site_name' content='I&ntilde;igo Valentin'/>
@@ -54,7 +55,7 @@
         <meta name='twitter:title' content='I&ntilde;igo Valentin'/>
         <meta name='twitter:description' content=''/>
         <meta name='twitter:image' content=''/>
-        <meta name='twitter:url' content='<?=$server?>'/>
+        <meta name='twitter:url' content='<?=$lserver?>'/>
         <meta name='robots' content='index follow'/>
     </head>
     <body>

+ 0 - 72
www/contact/index.php

@@ -1,72 +0,0 @@
-<?php
-    session_start();
-    $http_host = $_SERVER["HTTP_HOST"];
-    $doc_root = $_SERVER["DOCUMENT_ROOT"]; 
-    include $doc_root . "functions.php";
-    $proto = get_protocol();
-    $con = start_db();
-    $server = $proto . $http_host;
-    $lang = select_language();
-    $cur_section = "";
-    $cur_entry = "";
-?>
-<!DOCTYPE html>
-<html>
-    <head>
-        <meta content='text/html; charset=utf-8' http-equiv='content-type'/>
-        <meta charset='utf-8'/>
-        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
-        <title>I&ntilde;igo Valentin</title>
-        <link rel='shortcut icon' href='<?=$server?>/img/logo/favicon.ico'/>
-        <!-- CSS files -->
-        <style>
-<?php
-            include $doc_root . "css/ui.css";
-            include $doc_root . "css/home.css";
-?>
-        </style>
-        <!-- CSS for mobile version -->
-        <style media='(max-width : 990px)'>
-<?php
-            include $doc_root . "css/m/ui.css";
-            include $doc_root . "css/m/home.css";
-?>
-        </style>
-        <!-- Script files -->
-        <script type='text/javascript'>
-<?php
-            include $doc_root . "script/ui.js";
-?>
-        </script>
-        <!-- Meta tags -->
-        <link rel='canonical' href='<?=$server?>'/>
-        <link rel='author' href='<?=$server?>'/>
-        <link rel='publisher' href='<?=$server?>'/>
-        <meta name='description' content=''/>
-        <meta property='og:title' content='I&ntilde;igo Valentin'/>
-        <meta property='og:url' content='<?=$server?>'/>
-        <meta property='og:description' content=''/>
-        <meta property='og:image' content=''/>
-        <meta property='og:site_name' content='I&ntilde;igo Valentin'/>
-        <meta property='og:type' content='website'/>
-        <meta property='og:locale' content='<?=$lang?>'/>
-        <meta name='twitter:card' content='summary'/>
-        <meta name='twitter:title' content='I&ntilde;igo Valentin'/>
-        <meta name='twitter:description' content=''/>
-        <meta name='twitter:image' content=''/>
-        <meta name='twitter:url' content='<?=$server?>'/>
-        <meta name='robots' content='index follow'/>
-    </head>
-    <body>
-<?php
-        include $doc_root . "header.php";
-?>
-        <div id='content'>
-
-        </div> <!-- #content -->
-<?php
-        include $doc_root . "footer.php";
-        stats($con, $cur_section, $cur_entry);
-?>
-    </body>
-</html>

+ 62 - 0
www/css/blog.css

@@ -0,0 +1,62 @@
+/* Common elements */
+span.version_type{
+    padding: 0.05em;
+    border 0.1em solid #444444;
+    border-radius: 0.2em;
+}
+
+
+/* Projects main page */
+
+
+/* Project view page */
+
+div#content{
+    display: table;
+}
+
+div#content-row{
+    display: table-row;
+}
+
+div.content_cell{
+    display: table-cell;
+    padding: 1em;
+    vertical-align: top;
+}
+
+div#content_cell_main{
+    width: 80%;
+}
+
+div#content_cell_right{
+    width: 20%;
+}
+
+div#main_column div.iframe_container{
+    width: 100%;
+    text-align: center;
+}
+
+div#main_column iframe.project_embed{
+    background-color: #ffffff;
+    border-radius: 0.8em;
+    border: 0.1em solid #110022;
+    max-width: 90%;
+    max-height: 70em;
+}
+
+div#right_column{
+    transition: all .3s ease-in-out;
+}
+
+div#right_column div.url{
+    vertical-align: middle;
+}
+
+div#right_column div.url img{
+    width: 2em;
+    height: 2em;
+    vertical-align: middle;
+}
+

+ 22 - 0
www/css/home.css

@@ -6,3 +6,25 @@ div#logo{
 div#logo img{
     max-height: 7em;
 }
+
+div#content{
+    display: table;
+}
+
+div#content div#content_row{
+    display: table-row;
+}
+
+div#content div#content_row div.content_cell{
+    display: table-cell;
+    vertical-align: top;
+    margin:1em;
+}
+
+div#content div#content_row div#content_cell_profile{
+    width: 30%;
+}
+
+div#content div#content_row div#content_cell_content{
+    width: 70%;
+}

+ 0 - 0
www/css/m/blog.css


+ 0 - 0
www/css/m/profile.css


+ 0 - 0
www/css/m/project.css


+ 58 - 0
www/css/profile.css

@@ -0,0 +1,58 @@
+div#profile{
+    display: table;
+}
+
+div#profile div.table_row{
+    diplay: table-row;
+}
+
+div#profile div.table_row div.table_cell{
+    display: table-cell;
+    vertical-align: middle;
+}
+
+div#profile img{
+    max-width: 10em;
+    max-height: 10em;
+    border-radius: 1.2em;
+    margin: 1em;
+    border: 0.3em solid #000000;
+}
+
+div.cv_entry{
+    margin: 0.5em;
+}
+
+div.cv_entry h4.cv_entry_title{
+    padding-left: 1em;
+    font-weight: bold;
+    font-size: 130%;
+    border-bottom: 0.1em solid #00330077;
+    margin-bottom: 0em;
+    border-bottom-left-radius: 0.5em;
+}
+
+div.role{
+    margin: 0em 1em 0 1em;
+    border-left: 0.1em solid #00330077;
+}
+
+div.role h4{
+    border-bottom: 0.1em solid #00330077;
+    font-weight: bold;
+    font-size: 130%;
+    margin-left: 0;
+    margin-bottom: 0.2em;
+    padding: 0 1em 0 0.4em;
+    display: inline-block;
+}
+
+div.role h5{
+    font-style: italic;
+    margin: 0.5em 1em 0.5em 1em;
+}
+
+div.role p{
+    margin: 0.5em 2em 0 2em;
+    padding-bottom: 1em;
+}

+ 106 - 0
www/css/project.css

@@ -0,0 +1,106 @@
+/* Common elements */
+span.version_type{
+    padding: 0.05em;
+    border 0.1em solid #444444;
+    border-radius: 0.2em;
+}
+
+
+/* Projects main page */
+div#right_column div#advanced_search{
+    transition: all .3s ease-in-out;
+    overflow: hidden;
+    height: 0;
+}
+
+
+/* Project view page */
+
+div#content{
+    display: table;
+}
+
+div#content-row{
+    display: table-row;
+}
+
+div.content_cell{
+    display: table-cell;
+    padding: 1em;
+    vertical-align: top;
+}
+
+div#content_cell_main{
+    width: 80%;
+}
+
+div#content_cell_right{
+    width: 20%;
+}
+
+div#main_column div.iframe_container{
+    width: 100%;
+    text-align: center;
+}
+
+div#main_column iframe.project_embed{
+    background-color: #ffffff;
+    border-radius: 0.8em;
+    border: 0.1em solid #110022;
+    max-width: 90%;
+    max-height: 70em;
+}
+
+div#right_column{
+    transition: all .3s ease-in-out;
+}
+
+div#right_column div.url{
+    vertical-align: middle;
+}
+
+div#right_column div.url img{
+    width: 2em;
+    height: 2em;
+    vertical-align: middle;
+}
+
+div#right_column img#img_license{
+    max-height: 1.3em;
+}
+
+div#license{
+    display: none;
+    opacity: 0;
+    position: fixed;
+    transition: opacity .4s ease-in-out;
+    top: 3em;
+    left: 5em;
+    right: 5em;
+    bottom: 2em;
+}
+
+div#license h3 img.pointer{ /* Close icon*/
+    float: right;
+    height: 0.5em;
+    width: 0.5em;
+    margin-right: 1.2em;
+    margin-top: 0.2em;
+}
+
+div#license div.entry{
+    height: 30%;
+    margin: 0 0.5em 0.5em 0.5em;
+    overflow-y: scroll;
+}
+
+div#license div.entry h4{
+    margin: 0;
+}
+
+div#license div.entry div.license_text{
+    margin: 0;
+    
+}
+
+

+ 79 - 39
www/css/ui.css

@@ -2,7 +2,7 @@
  * Fonts used across the page     *
  **********************************/
 
-/* Font used for inputs */
+/* Font */
 @font-face {
     font-family: 'Nunito'; 
     src:  url('/fonts/Nunito-Regular.ttf'); 
@@ -27,10 +27,37 @@ a{
 }
 
 /* Elements fith bold font */
-span.bold{
+.bold{
     font-weight: bold;
 }
 
+.pointer {
+    cursor: pointer;
+}
+
+/* Hidden elements */
+.hidden{
+    display: none;
+}
+
+img.slider{
+    width: 1em;
+    height: 1em;
+    vertial-align: middle;
+    transition: all .3s ease-in-out;
+}
+
+div#cover{
+    display: none;
+    background-color: #000000bb;
+    opacity: 0;
+    transition: opacity .3s ease-in-out;
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
 
 
 /**********************************
@@ -55,7 +82,7 @@ nav#header{
     font-size: 110%;
 }
 
-/* Dummy <span> to use as vertical separator between links */
+/* Dummy span to use as vertical separator between links */
 nav#header span.link_separator{
     border-left: 0.077em solid rgba(200, 200, 200, .5);
 }
@@ -167,7 +194,7 @@ div#footer td#footer_center{
 /* Right area of the footer */
 div#footer td#footer_right{
     width: 33%;
-    text-align: right;
+    text-align: center;
 }
 
 /* Icons for social media */
@@ -207,22 +234,43 @@ div#content{
     margin-bottom: 1.875em;
 }
 
-/* .lighted is a style-only class to make contrast against the dark background */
-.lighted{
-    background-color: #898989;
-    border: 0.063em solid #77777;
-    border-radius: 0.15em;
-  padding: 0.5em;
-    box-shadow: 0 0 0.75em #888888;
+div.section{
+    margin: 0.5em;
+    padding: 0;
+    background-color: #ffffff;
+    border: 0.063em solid #005500;
+    border-radius: 1em 1em;
+    box-shadow: 0 0 0.75em #003300;
+    overflow: hidden;
 }
 
-/* Style only class. Is the title of a .lighted element */
-.section_title{
+div.section h3.section_title{
+    font-weight: bold;
+    color: #ccddff;
+    text_align: left;
+    padding: 0.2em 0.4em 0.2em 0.4em;
+    margin: 0;
+    text-shadow: 0 0 0.4em #000022;
     font-size: 150%;
-    margin: 0.625em 0 0.438em 0.625em;
-    text-shadow: 0 0 0.75em #dddddd;
+    background: linear-gradient(to bottom, #1e9957 0%,#29d889 89%,#20ca7c 97%,#7de8b9 100%);
+}
+
+/* .entry is a style only class to show content. Must be allways children os a .lighted element */
+div.section div.entry{
+    border: 0.1em solid #aaffaa00;
+    border-radius: 0.3em;
+    padding: 0.5em;
+    margin: 0.5em;
+    transition: all .4s ease-in-out;
+}
+
+div.section div.entry_list:hover{
+    background-color: #efffef;
+    border: 0.1em solid #aaffaaff;
 }
 
+
+
 /* Style for the mathces in search results */
 span.result_counter{
     margin-left: 1.2em;
@@ -231,21 +279,6 @@ span.result_counter{
     font-style: italic;
 }
 
-/* .entry is a style only class to show content. Must be allways children os a .lighted element */
-.entry{
-    transition: all .3s ease-in-out;
-    margin: 1% 0.438em 0 0.438em;
-    background-color: #cccccc;
-    border: 0.143em solid #555555;
-    border-radius: 0.314em;
-    padding: 0.5em;
-    box-shadow: 0 0 0.5em #333333;
-}
-
-.entry:hover{
-    background-color: #eeeeee;
-}
-
 /* Shown when no records on database */
 div.unavailable{
     margin-left: 4em;
@@ -328,22 +361,29 @@ input[type=button]{
 /* Style for textboxes */
 input[type=text]{
     font-weight: bold;
-    color: #444444;
-    background-color: #bbbbbb;
-  border: 0.286em solid #dddddd; 
+    color: #224422;
+    background-color: #bbccbb;
+    border: 0.286em solid #ddeedd; 
     border-color: #dddddd;
     border-radius: 0.429em;
     padding: 0.25em;
     text-shadow: 0 0 0.5em #ffffff;
 }
 
+input[type=checkbox]{
+    width: 1em;
+    height: 1em;
+    border-radius: 0.2em;
+    border: 0.063em solid #005500;
+}
+
 /* Style for textareas */
 textarea{
     width: 80%;
     font-weight: bold;
     color: #444444;
     background-color: #bbbbbb;
-  border: 0.286em solid #dddddd; 
+    border: 0.286em solid #dddddd; 
     border-color: #dddddd;
     border-radius: 0.286em;
     padding: 0.25em;
@@ -356,7 +396,7 @@ select{
     font-weight: bold;
     color: #444444;
     background-color: #bbbbbb;
-  border: 0.286em solid #dddddd; 
+    border: 0.286em solid #dddddd; 
     border-radius: 0.286em;
     padding: 0.25em;
     text-shadow: 0 0 0.5em #ffffff;
@@ -437,7 +477,7 @@ img.comment_image{
     border: 0.077em solid black;
     max-height: 6em;
     max-width: 60%;
-  display: block;
+    display: block;
     vertical-align: top;
     border: 0.133em solid black;
     margin-left: 0.938em;
@@ -530,7 +570,7 @@ table#pager td.pager_current{
     border: 0.077em solid #444444;
 }
 
-/* Dummy <td> that contains "..." */
+/* Dummy td that contains "..." */
 table#pager td.pager_ellipse{
     background-color: initial;
     border: initial;
@@ -559,7 +599,7 @@ span#span_download{
     display: none;
 }
 
-/* Hidden <div> to confirm downloading */
+/* Hidden div to confirm downloading */
 div.license{
     box-shadow: 0 0 1.2em rgba(120, 120, 120, 1);
     display: none;
@@ -575,7 +615,7 @@ div.license{
     border-radius: 1.250em;
 }
 
-/* <div> to dimm the screen */
+/* div to dimm the screen */
 #screen_cover{
     position: fixed;
     top: 0;

+ 0 - 72
www/cv/index.php

@@ -1,72 +0,0 @@
-<?php
-    session_start();
-    $http_host = $_SERVER["HTTP_HOST"];
-    $doc_root = $_SERVER["DOCUMENT_ROOT"]; 
-    include $doc_root . "functions.php";
-    $proto = get_protocol();
-    $con = start_db();
-    $server = $proto . $http_host;
-    $lang = select_language();
-    $cur_section = "";
-    $cur_entry = "";
-?>
-<!DOCTYPE html>
-<html>
-    <head>
-        <meta content='text/html; charset=utf-8' http-equiv='content-type'/>
-        <meta charset='utf-8'/>
-        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
-        <title>I&ntilde;igo Valentin</title>
-        <link rel='shortcut icon' href='<?=$server?>/img/logo/favicon.ico'/>
-        <!-- CSS files -->
-        <style>
-<?php
-            include $doc_root . "css/ui.css";
-            include $doc_root . "css/home.css";
-?>
-        </style>
-        <!-- CSS for mobile version -->
-        <style media='(max-width : 990px)'>
-<?php
-            include $doc_root . "css/m/ui.css";
-            include $doc_root . "css/m/home.css";
-?>
-        </style>
-        <!-- Script files -->
-        <script type='text/javascript'>
-<?php
-            include $doc_root . "script/ui.js";
-?>
-        </script>
-        <!-- Meta tags -->
-        <link rel='canonical' href='<?=$server?>'/>
-        <link rel='author' href='<?=$server?>'/>
-        <link rel='publisher' href='<?=$server?>'/>
-        <meta name='description' content=''/>
-        <meta property='og:title' content='I&ntilde;igo Valentin'/>
-        <meta property='og:url' content='<?=$server?>'/>
-        <meta property='og:description' content=''/>
-        <meta property='og:image' content=''/>
-        <meta property='og:site_name' content='I&ntilde;igo Valentin'/>
-        <meta property='og:type' content='website'/>
-        <meta property='og:locale' content='<?=$lang?>'/>
-        <meta name='twitter:card' content='summary'/>
-        <meta name='twitter:title' content='I&ntilde;igo Valentin'/>
-        <meta name='twitter:description' content=''/>
-        <meta name='twitter:image' content=''/>
-        <meta name='twitter:url' content='<?=$server?>'/>
-        <meta name='robots' content='index follow'/>
-    </head>
-    <body>
-<?php
-        include $doc_root . "header.php";
-?>
-        <div id='content'>
-
-        </div> <!-- #content -->
-<?php
-        include $doc_root . "footer.php";
-        stats($con, $cur_section, $cur_entry);
-?>
-    </body>
-</html>

+ 101 - 0
www/error/403.php

@@ -0,0 +1,101 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"]; 
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $lang = select_language();
+    $lserver = $server . "/" . $lang;
+    $cur_section = "error";
+    $cur_entry = "403";
+?>
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta content='text/html; charset=windows-1252' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'>
+        <title>Error - I&ntilde;igo Valentin</title>
+        <link rel='shortcut icon' href='<?=$lserver?>/img/logo/favicon.ico'>
+        <!-- CSS files -->
+        <link rel='stylesheet' type='text/css' href='/css/ui.css'/>
+        <style>
+            table#error-code{
+                margin-top:        5em;
+                margin-left:     10%;
+            }
+            td#error-number{
+                font-size:        300%;
+                border-right:    0.063em solid #000000;
+                padding-right:    1.875em;
+                padding-left:    1.250em;
+            }
+            td#error-tag{
+                font-size:        110%;
+                padding-left:    1.750em;
+                padding-right:    1.250em;
+            }
+            div#error-message{
+                width:            80%;
+                margin-left:    10%;
+            }
+            @media (max-width: 990px){
+                table#error-code{
+                    width:            90%;
+                    margin-left:    auto;
+                    margin-right:    auto;
+                    margin-top:        0;
+                    margin-bottom:    0;
+                }
+                div#error-message{
+                    width:            90%;
+                    margin-left:    auto;
+                    margin-right:    auto;
+                    margin-top:        0;
+                    margin-bottom:    0;
+                }
+            }
+        </style>
+        <meta name="robots" content="noindex follow"/>
+    </head>
+    <body>
+<?php
+        include $doc_root . "header.php";
+?>
+        <div class='section section_no_title' >
+            <table id='error-code' class='entry'>
+                <tr>
+                    <td id='error-number'>
+                        403
+                    </td>
+                    <td id='error-tag'>
+                        ERROR<br/>CODE
+                    </td>
+                <tr>
+            </table>
+            <br/><br/><br/><br/>
+            <div id='error-message' class='entry'>
+                <h2>Forbidden</h2>
+                This means you are trying to view something you are not allowed to, security wise.
+                <br/><br/>
+                Try a solution:
+                <ul>
+                    <li>
+                        <a href='javascript: location.reload();'>Try again.</a>
+                    </li>
+                    <li>
+                        <a href='javascript: history.go(-1);'>Go to the previous page.</a>
+                    </li>
+                    <li>
+                        <a href='<?=$lserver?>/'>Go to the main page</a>
+                    </li>
+                </ul>
+            </div> <!-- #error_message -->
+        </div> <!-- .section -->
+<?php
+        include $doc_root . "footer.php";
+        stats($con, $cur_section, $cur_entry);
+?>
+    </body>
+</html>

+ 101 - 0
www/error/404.php

@@ -0,0 +1,101 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"]; 
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $lang = select_language();
+    $lserver = $server . "/" . $lang;
+    $cur_section = "error";
+    $cur_entry = "404";
+?>
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta content='text/html; charset=windows-1252' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'>
+        <title>Error - I&ntilde;igo Valentin</title>
+        <link rel='shortcut icon' href='<?=$lserver?>/img/logo/favicon.ico'>
+        <!-- CSS files -->
+        <link rel='stylesheet' type='text/css' href='/css/ui.css'/>
+        <style>
+            table#error-code{
+                margin-top:        5em;
+                margin-left:     10%;
+            }
+            td#error-number{
+                font-size:        300%;
+                border-right:    0.063em solid #000000;
+                padding-right:    1.875em;
+                padding-left:    1.250em;
+            }
+            td#error-tag{
+                font-size:        110%;
+                padding-left:    1.750em;
+                padding-right:    1.250em;
+            }
+            div#error-message{
+                width:            80%;
+                margin-left:    10%;
+            }
+            @media (max-width: 990px){
+                table#error-code{
+                    width:            90%;
+                    margin-left:    auto;
+                    margin-right:    auto;
+                    margin-top:        0;
+                    margin-bottom:    0;
+                }
+                div#error-message{
+                    width:            90%;
+                    margin-left:    auto;
+                    margin-right:    auto;
+                    margin-top:        0;
+                    margin-bottom:    0;
+                }
+            }
+        </style>
+        <meta name="robots" content="noindex follow"/>
+    </head>
+    <body>
+<?php
+        include $doc_root . "header.php";
+?>
+        <div class='section section_no_title' >
+            <table id='error-code' class='entry'>
+                <tr>
+                    <td id='error-number'>
+                        404
+                    </td>
+                    <td id='error-tag'>
+                        ERROR<br/>CODE
+                    </td>
+                <tr>
+            </table>
+            <br/><br/><br/><br/>
+            <div id='error-message' class='entry'>
+                <h2>Not Found</h2>
+                This means that, either you are looking for something that doesn't exist or that we missed something.
+                <br/><br/>
+                Try a solution:
+                <ul>
+                    <li>
+                        <a href='javascript: location.reload();'>Try again.</a>
+                    </li>
+                    <li>
+                        <a href='javascript: history.go(-1);'>Go to the previous page.</a>
+                    </li>
+                    <li>
+                        <a href='<?=$lserver?>/'>Go to the main page</a>
+                    </li>
+                </ul>
+            </div> <!-- #error_message -->
+        </div> <!-- .section -->
+<?php
+        include $doc_root . "footer.php";
+        stats($con, $cur_section, $cur_entry);
+?>
+    </body>
+</html>

+ 101 - 0
www/error/500.php

@@ -0,0 +1,101 @@
+<?php
+    session_start();
+    $http_host = $_SERVER["HTTP_HOST"];
+    $doc_root = $_SERVER["DOCUMENT_ROOT"];
+    include $doc_root . "functions.php";
+    $proto = get_protocol();
+    $con = start_db();
+    $server = $proto . $http_host;
+    $lang = select_language();
+    $lserver = $server . "/" . $lang;
+    $cur_section = "error";
+    $cur_entry = "500";
+?>
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta content='text/html; charset=windows-1252' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'>
+        <title>Error - I&ntilde;igo Valentin</title>
+        <link rel='shortcut icon' href='<?=$lserver?>/img/logo/favicon.ico'>
+        <!-- CSS files -->
+        <link rel='stylesheet' type='text/css' href='/css/ui.css'/>
+        <style>
+            table#error-code{
+                margin-top:        5em;
+                margin-left:     10%;
+            }
+            td#error-number{
+                font-size:        300%;
+                border-right:    0.063em solid #000000;
+                padding-right:    1.875em;
+                padding-left:    1.250em;
+            }
+            td#error-tag{
+                font-size:        110%;
+                padding-left:    1.750em;
+                padding-right:    1.250em;
+            }
+            div#error-message{
+                width:            80%;
+                margin-left:    10%;
+            }
+            @media (max-width: 990px){
+                table#error-code{
+                    width:            90%;
+                    margin-left:    auto;
+                    margin-right:    auto;
+                    margin-top:        0;
+                    margin-bottom:    0;
+                }
+                div#error-message{
+                    width:            90%;
+                    margin-left:    auto;
+                    margin-right:    auto;
+                    margin-top:        0;
+                    margin-bottom:    0;
+                }
+            }
+        </style>
+        <meta name="robots" content="noindex follow"/>
+    </head>
+    <body>
+<?php
+        include $doc_root . "header.php";
+?>
+        <div class='section section_no_title' >
+            <table id='error-code' class='entry'>
+                <tr>
+                    <td id='error-number'>
+                        500
+                    </td>
+                    <td id='error-tag'>
+                        ERROR<br/>CODE
+                    </td>
+                <tr>
+            </table>
+            <br/><br/><br/><br/>
+            <div id='error-message' class='entry'>
+               <h2>Internal Server Error</h2>
+                We messed up. Something is wrong with the server right now, and hopefully it will be fixed soon.
+                <br/><br/>
+                Try a solution:
+                <ul>
+                    <li>
+                        <a href='javascript: location.reload();'>Try again.</a>
+                    </li>
+                    <li>
+                        <a href='javascript: history.go(-1);'>Go to the previous page.</a>
+                    </li>
+                    <li>
+                        <a href='<?=$lserver?>/'>Go to the main page</a>
+                    </li>
+                </ul>
+            </div> <!-- #error_message -->
+        </div> <!-- .section -->
+<?php
+        include $doc_root . "footer.php";
+        stats($con, $cur_section, $cur_entry);
+?>
+    </body>
+</html>

+ 17 - 7
www/footer.php

@@ -4,21 +4,31 @@
         <tr>
             <td id='footer_left'>
                 <span id='footer_follow' class='desktop'><?=text($con, "FOOTER_FOLLOW", $lang);?></span>
-                <a title='Github' href='https://www.github.com/u/Seavenois'><img class='footer_social_icon' src='<?=$server?>/img/social/github.gif' alt='Github'/></a>
-                <a title='Facebook' href='https://www.facebook.com/ivalentin'><img class='footer_social_icon' src='<?=$server?>/img/social/facebook.gif' alt='Facebook'/></a>
-                <a title='Google+' href='https://plus.google.com/108048773942421710294/'><img class='footer_social_icon' src='<?=$server?>/img/social/googleplus.gif' alt='Google+'/></a>
-                <a title='RSS' href='<?=$server?>/feed/'><img class='footer_social_icon' src='<?=$server?>/img/social/rss.gif' alt='RSS'/></a>
+<?php
+                $q_social = mysqli_query($con, "SELECT title, icon, url FROM social WHERE visible = 1 ORDER BY idx;");
+                while ($r_social = mysqli_fetch_array($q_social)){
+?>
+                    <a title='<?=text($con, $r_social["title"], $lang)?>' href='<?=$r_social["url"]?>'>
+                        <img class='footer_social_icon' src='<?=$lserver?>/img/social/x2/<?=$r_social["icon"]?>' alt='<?=text($con, $r_social["title"], $lang)?>' title='<?=text($con, $r_social["title"], $lang)?>'/>
+                    </a>
+<?php
+                }
+?>
             </td>
             <td id='footer_center'><?=text($con, "FOOTER_COPY", $lang);?></td>
             <td id='footer_right'>
-                <a href='<?=$server?>/help/'><?=text($con, "FOOTER_HELP", $lang);?></a>
+                <a href='<?=$lserver?>/help/'><?=text($con, "FOOTER_HELP", $lang);?></a>
                 <br/><br/>
 <?php
                 $q_lang = mysqli_query($con, "SELECT * FROM lang WHERE active = 1;");
                 while ($r = mysqli_fetch_array($q_lang)){
-                    // TODO: href
+                    $uri = $_SERVER["REQUEST_URI"];
+                    if (substr($uri, 3, 1) == '/'){
+                        $uri = substr($uri, 3);
+                    }
+                    $dest = $server . "/" . $r["code"] . $uri;
 ?>
-                    <a class='lang' href='#'><img src='<?=$server?>/img/lang/<?=$r["code"]?>.gif' alt='<?=$r['name']?>' /></a>
+                    <a class='lang' href='<?=$dest?>'><img src='<?=$lserver?>/img/lang/<?=$r["code"]?>.gif' alt='<?=$r['name']?>' /></a>
 <?php
                 }
 ?>

+ 44 - 21
www/functions.php

@@ -66,29 +66,46 @@
      *****************************************************/
     // TODO: Redo using database
     function select_language(){
-        //Try to read cookie.
-        header("Cache-control: private");
-        if (isSet($_COOKIE["lang"])){
-            $lang = $_COOKIE["lang"];
+
+        // Check for language in uri
+        $lang = substr($_SERVER['REQUEST_URI'], 1, 2);
+        if ($lang == "es" || $lang == "en" || $lang == "eu"){
+            return $lang;
+        }
+        else{
+            // Language is not in url. Find it out and redirect
+            // Is passed on the URL?
+            $lang = $_GET["lang"];
             if ($lang == "es" || $lang == "en" || $lang == "eu"){
+                header("Location: " . get_protocol() . $_SERVER["HTTP_HOST"] . "/" . $lang . $_SERVER["REQUEST_URI"]);
                 return $lang;
             }
-            else{
-                return "es";
-            }
-        }
 
-        //If no cookie, select from client browser preferences.
-        else{
-            $available_languages = array("en", "eu", "es");
-            $langs = prefered_language($available_languages, $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
-            $lang = $langs[0];
-            if ($lang != "es" && $lang != "en" && $lang != "eu"){
-                return "es";
+            // Try to read cookie.
+            header("Cache-control: private");
+            if (isSet($_COOKIE["lang"])){
+                $lang = $_COOKIE["lang"];
+                if ($lang == "es" || $lang == "en" || $lang == "eu"){
+                    header("Location: " . get_protocol() . $_SERVER["HTTP_HOST"] . "/" . $lang . $_SERVER["REQUEST_URI"]);
+                    return $lang;
+                }
             }
+
+            //If no cookie, select from client browser preferences.
             else{
-                return $langs[0];
+                $available_languages = array("en", "eu", "es");
+                $langs = prefered_language($available_languages, $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
+                $lang = $langs[0];
+                if ($lang == "es" || $lang == "en" || $lang == "eu"){
+                    header("Location: " . get_protocol() . $_SERVER["HTTP_HOST"] . "/" . $lang . $_SERVER["REQUEST_URI"]);
+                    return $lang;
+                }
             }
+
+            // If no method was succesfull, default language
+            $lang = "es";
+            header("Location: " . get_protocol() . $_SERVER["HTTP_HOST"] . "/" . $lang . $_SERVER["REQUEST_URI"]);
+            return $lang;
         }
     }
 
@@ -210,7 +227,8 @@
         $str = str_replace(" ", "-", $str);
         return $str;
     }
-    
+
+
     /*****************************************************
      * Retrieves a text fragment from the database.      *
      *                                                   *
@@ -221,9 +239,14 @@
      * @return: (string): Text.                          *
      *****************************************************/
     function text($con, $id, $lang){
-        error_log("SELECT text FROM text WHERE id = '$id' AND lang = '$lang';");
-        $q = mysqli_query($con, "SELECT text FROM text WHERE id = '$id' AND lang = '$lang';");
-        return mysqli_fetch_array($q)["text"];
+        $q = mysqli_query($con, "SELECT text, file FROM text WHERE id = '$id' AND lang = '$lang';");
+        $r = mysqli_fetch_array($q);
+        if (strlen($r["file"]) > 0){
+            return file_get_contents($_SERVER["DOCUMENT_ROOT"] . "string/" . $r["file"]);
+        }
+        else{
+            return $r["text"];
+        }
     }
 
 
@@ -274,7 +297,7 @@
             return $text;
         }
         $cut = substr($text, 0, strpos($text, " ", $length));
-        $cut = closeTags($cut);
+        $cut = close_tags($cut);
         if (strlen($cut) == 0){
             $cut = $text;
         }

+ 9 - 9
www/header.php

@@ -1,29 +1,29 @@
 <div id='header_parent'>
     <nav id='header'>
-        <a href='<?=$server?>/'>
+        <a href='<?=$lserver?>/'>
             <span class='header_container'>
-                <span class='header_icon_container'><img class='header_icon' alt='<?=text($con, "HEADER_HOME", $lang);?>' src='<?=$server?>/img/icon/home.gif'/></span>
+                <span class='header_icon_container'><img class='header_icon' alt='<?=text($con, "HEADER_HOME", $lang);?>' src='<?=$lserver?>/img/icon/home.gif'/></span>
                 <?=text($con, "HEADER_HOME", $lang);?>
             </span>
         </a>
         <span class='link_separator desktop'></span>
-        <a href='<?=$server?>/me/'>
+        <a href='<?=$lserver?>/profile/'>
             <span class='header_container'>
-                <span class='header_icon_container'><img class='header_icon' alt='<?=text($con, "HEADER_ME", $lang);?>' src='<?=$server?>/img/icon/me.gif'/></span>
-                <?=text($con, "HEADER_ME", $lang);?>
+                <span class='header_icon_container'><img class='header_icon' alt='<?=text($con, "HEADER_ME", $lang);?>' src='<?=$lserver?>/img/icon/profile.gif'/></span>
+                <?=text($con, "HEADER_PROFILE", $lang);?>
             </span>
         </a>
         <span class='link_separator desktop'></span>
-        <a href='<?=$server?>/projects/'>
+        <a href='<?=$lserver?>/project/'>
             <span class='header_container'>
-                <span class='header_icon_container'><img class='header_icon' alt='<?=text($con, "HEADER_PROJECTS", $lang);?>' src='<?=$server?>/img/icon/projects.gif'/></span>
+                <span class='header_icon_container'><img class='header_icon' alt='<?=text($con, "HEADER_PROJECTS", $lang);?>' src='<?=$lserver?>/img/icon/project.gif'/></span>
                 <?=text($con, "HEADER_PROJECTS", $lang);?>
             </span>
         </a>
         <span class='link_separator desktop'></span>
-        <a href='<?=$server?>/blog/'>
+        <a href='<?=$lserver?>/blog/'>
             <span class='header_container'>
-                <span class='header_icon_container'><img class='header_icon' alt='<?=text($con, "HEADER_BLOG", $lang);?>' src='<?=$server?>/img/icon/blog.gif'/></span>
+                <span class='header_icon_container'><img class='header_icon' alt='<?=text($con, "HEADER_BLOG", $lang);?>' src='<?=$lserver?>/img/icon/blog.gif'/></span>
                 <?=text($con, "HEADER_BLOG", $lang);?>
             </span>
         </a>

+ 7 - 6
www/help/index.php

@@ -7,6 +7,7 @@
     $con = start_db();
     $server = $proto . $http_host;
     $lang = select_language();
+    $lserver = $server . "/" . $lang;
     $cur_section = "";
     $cur_entry = "";
 ?>
@@ -17,7 +18,7 @@
         <meta charset='utf-8'/>
         <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
         <title>I&ntilde;igo Valentin</title>
-        <link rel='shortcut icon' href='<?=$server?>/img/logo/favicon.ico'/>
+        <link rel='shortcut icon' href='<?=$lserver?>/img/logo/favicon.ico'/>
         <!-- CSS files -->
         <style>
 <?php
@@ -39,12 +40,12 @@
 ?>
         </script>
         <!-- Meta tags -->
-        <link rel='canonical' href='<?=$server?>'/>
-        <link rel='author' href='<?=$server?>'/>
-        <link rel='publisher' href='<?=$server?>'/>
+        <link rel='canonical' href='<?=$lserver?>'/>
+        <link rel='author' href='<?=$lserver?>'/>
+        <link rel='publisher' href='<?=$lserver?>'/>
         <meta name='description' content=''/>
         <meta property='og:title' content='I&ntilde;igo Valentin'/>
-        <meta property='og:url' content='<?=$server?>'/>
+        <meta property='og:url' content='<?=$lserver?>'/>
         <meta property='og:description' content=''/>
         <meta property='og:image' content=''/>
         <meta property='og:site_name' content='I&ntilde;igo Valentin'/>
@@ -54,7 +55,7 @@
         <meta name='twitter:title' content='I&ntilde;igo Valentin'/>
         <meta name='twitter:description' content=''/>
         <meta name='twitter:image' content=''/>
-        <meta name='twitter:url' content='<?=$server?>'/>
+        <meta name='twitter:url' content='<?=$lserver?>'/>
         <meta name='robots' content='index follow'/>
     </head>
     <body>

+ 0 - 0
www/img/icon/me.gif → www/img/icon/profile.gif


BIN
www/img/icon/project.gif


BIN
www/img/icon/x1/blog.gif


BIN
www/img/icon/x1/home.gif


BIN
www/img/icon/x1/profile.gif


BIN
www/img/icon/x1/project.gif


BIN
www/img/icon/x1/projects.gif


BIN
www/img/icon/x2/blog.gif


BIN
www/img/icon/x2/home.gif


BIN
www/img/icon/x2/profile.gif


BIN
www/img/icon/x2/project.gif


BIN
www/img/icon/x2/projects.gif


BIN
www/img/icon/x3/blog.gif


BIN
www/img/icon/x3/home.gif


BIN
www/img/icon/x3/profile.gif


BIN
www/img/icon/x3/project.gif


BIN
www/img/icon/x3/projects.gif


BIN
www/img/icon/x4/blog.gif


BIN
www/img/icon/x4/home.gif


BIN
www/img/icon/x4/profile.gif


BIN
www/img/icon/x4/project.gif


BIN
www/img/icon/x4/projects.gif


BIN
www/img/icon/x5/blog.gif


BIN
www/img/icon/x5/home.gif


BIN
www/img/icon/x5/profile.gif


BIN
www/img/icon/x5/project.gif


BIN
www/img/icon/x5/projects.gif


BIN
www/img/icon/x6/blog.gif


BIN
www/img/icon/x6/home.gif


BIN
www/img/icon/x6/profile.gif


Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff