PageRenderTime 55ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/model/project.php

https://github.com/alugo/Goteo
PHP | 2348 lines | 1652 code | 306 blank | 390 comment | 266 complexity | d94bad9556a186ebb76aecce3f486f7b MD5 | raw file
Possible License(s): AGPL-1.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * Copyright (C) 2012 Platoniq y Fundación Fuentes Abiertas (see README for details)
  4. * This file is part of Goteo.
  5. *
  6. * Goteo is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Goteo is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with Goteo. If not, see <http://www.gnu.org/licenses/agpl.txt>.
  18. *
  19. */
  20. namespace Goteo\Model {
  21. use Goteo\Core\ACL,
  22. Goteo\Library\Check,
  23. Goteo\Library\Text,
  24. Goteo\Model\User,
  25. Goteo\Model\Image,
  26. Goteo\Model\Message;
  27. class Project extends \Goteo\Core\Model {
  28. public
  29. $id = null,
  30. $dontsave = false,
  31. $owner, // User who created it
  32. $node, // Node this project belongs to
  33. $status,
  34. $progress, // puntuation %
  35. $amount, // Current donated amount
  36. $user, // owner's user information
  37. // Register contract data
  38. $contract_name, // Nombre y apellidos del responsable del proyecto
  39. $contract_nif, // Guardar sin espacios ni puntos ni guiones
  40. $contract_email, // cuenta paypal
  41. $phone, // guardar sin espacios ni puntos
  42. // Para marcar física o jurídica
  43. $contract_entity = false, // false = física (persona) true = jurídica (entidad)
  44. // Para persona física
  45. $contract_birthdate,
  46. // Para entidad jurídica
  47. $entity_office, // cargo del responsable dentro de la entidad
  48. $entity_name, // denomincion social de la entidad
  49. $entity_cif, // CIF de la entidad
  50. // Campos de Domicilio: Igual para persona o entidad
  51. $address,
  52. $zipcode,
  53. $location, // owner's location
  54. $country,
  55. // Domicilio postal
  56. $secondary_address = false, // si es diferente al domicilio fiscal
  57. $post_address = null,
  58. $post_zipcode = null,
  59. $post_location = null,
  60. $post_country = null,
  61. // Edit project description
  62. $name,
  63. $subtitle,
  64. $lang = 'es',
  65. $image,
  66. $gallery = array(), // array de instancias image de project_image
  67. $secGallery = array(), // array de instancias image de project_image (secundarias)
  68. $description,
  69. $motivation,
  70. $video, // video de motivacion
  71. $video_usubs, // universal subtitles para el video de motivacion
  72. $about,
  73. $goal,
  74. $related,
  75. $reward, // nueva sección, solo editable por admines y traductores
  76. $categories = array(),
  77. $media, // video principal
  78. $media_usubs, // universal subtitles para el video principal
  79. $keywords, // por ahora se guarda en texto tal cual
  80. $currently, // Current development status of the project
  81. $project_location, // project execution location
  82. $scope, // ambito de alcance
  83. $translate, // si se puede traducir (bool)
  84. // costs
  85. $costs = array(), // project\cost instances with type
  86. $schedule, // picture of the costs schedule
  87. $resource, // other current resources
  88. // Rewards
  89. $social_rewards = array(), // instances of project\reward for the public (collective type)
  90. $individual_rewards = array(), // instances of project\reward for investors (individual type)
  91. // Collaborations
  92. $supports = array(), // instances of project\support
  93. // Comment
  94. $comment, // Comentario para los admin introducido por el usuario
  95. //Operative purpose properties
  96. $mincost = 0,
  97. $maxcost = 0,
  98. //Obtenido, Días, Cofinanciadores
  99. $invested = 0, //cantidad de inversión
  100. $days = 0, //para 40 desde la publicación o para 80 si no está caducado
  101. $investors = array(), // aportes individuales a este proyecto
  102. $num_investors = 0, // numero de usuarios que han aportado
  103. $round = 0, // para ver si ya está en la fase de los 40 a los 80
  104. $passed = null, // para ver si hemos hecho los eventos de paso a segunda ronda
  105. $willpass = null, // fecha final de primera ronda
  106. $errors = array(), // para los fallos en los datos
  107. $okeys = array(), // para los campos que estan ok
  108. // para puntuacion
  109. $score = 0, //puntos
  110. $max = 0, // maximo de puntos
  111. $messages = array(), // mensajes de los usuarios hilos con hijos
  112. $finishable = false, // llega al progresso mínimo para enviar a revision
  113. $tagmark = null; // banderolo a mostrar
  114. /**
  115. * Sobrecarga de métodos 'getter'.
  116. *
  117. * @param type string $name
  118. * @return type mixed
  119. */
  120. public function __get ($name) {
  121. if($name == "allowpp") {
  122. return Project\Account::getAllowpp($this->id);
  123. }
  124. if($name == "budget") {
  125. return self::calcCosts($this->id);
  126. }
  127. return $this->$name;
  128. }
  129. /**
  130. * Inserta un proyecto con los datos mínimos
  131. *
  132. * @param array $data
  133. * @return boolean
  134. */
  135. public function create ($node = \GOTEO_NODE, &$errors = array()) {
  136. $user = $_SESSION['user']->id;
  137. if (empty($user)) {
  138. return false;
  139. }
  140. // cojemos el número de proyecto de este usuario
  141. $query = self::query("SELECT COUNT(id) as num FROM project WHERE owner = ?", array($user));
  142. if ($now = $query->fetchObject())
  143. $num = $now->num + 1;
  144. else
  145. $num = 1;
  146. // datos del usuario que van por defecto: name->contract_name, location->location
  147. $userProfile = User::get($user);
  148. // datos del userpersonal por defecto a los cammpos del paso 2
  149. $userPersonal = User::getPersonal($user);
  150. $values = array(
  151. ':id' => md5($user.'-'.$num),
  152. ':name' => Text::_("El nuevo proyecto de ").$userProfile->name,
  153. ':lang' => 'es',
  154. ':status' => 1,
  155. ':progress' => 0,
  156. ':owner' => $user,
  157. ':node' => $node,
  158. ':amount' => 0,
  159. ':days' => 0,
  160. ':created' => date('Y-m-d'),
  161. ':contract_name' => ($userPersonal->contract_name) ?
  162. $userPersonal->contract_name :
  163. $userProfile->name,
  164. ':contract_nif' => $userPersonal->contract_nif,
  165. ':phone' => $userPersonal->phone,
  166. ':address' => $userPersonal->address,
  167. ':zipcode' => $userPersonal->zipcode,
  168. ':location' => ($userPersonal->location) ?
  169. $userPersonal->location :
  170. $userProfile->location,
  171. ':country' => ($userPersonal->country) ?
  172. $userPersonal->country :
  173. Check::country(),
  174. ':project_location' => ($userPersonal->location) ?
  175. $userPersonal->location :
  176. $userProfile->location,
  177. );
  178. $campos = array();
  179. foreach (\array_keys($values) as $campo) {
  180. $campos[] = \str_replace(':', '', $campo);
  181. }
  182. $sql = "REPLACE INTO project (" . implode(',', $campos) . ")
  183. VALUES (" . implode(',', \array_keys($values)) . ")";
  184. try {
  185. self::query($sql, $values);
  186. foreach ($campos as $campo) {
  187. $this->$campo = $values[":$campo"];
  188. }
  189. return $this->id;
  190. } catch (\PDOException $e) {
  191. $errors[] = "ERROR al crear un nuevo proyecto<br />$sql<br /><pre>" . print_r($values, 1) . "</pre>";
  192. \trace($this);
  193. die($errors[0]);
  194. return false;
  195. }
  196. }
  197. /*
  198. * Cargamos los datos del proyecto
  199. */
  200. public static function get($id, $lang = null) {
  201. try {
  202. // metemos los datos del proyecto en la instancia
  203. $query = self::query("SELECT * FROM project WHERE id = ?", array($id));
  204. $project = $query->fetchObject(__CLASS__);
  205. if (!$project instanceof \Goteo\Model\Project) {
  206. throw new \Goteo\Core\Error('404', Text::html('fatal-error-project'));
  207. }
  208. // si recibimos lang y no es el idioma original del proyecto, ponemos la traducción y mantenemos para el resto de contenido
  209. if ($lang == $project->lang) {
  210. $lang = null;
  211. } elseif (!empty($lang)) {
  212. $sql = "
  213. SELECT
  214. IFNULL(project_lang.description, project.description) as description,
  215. IFNULL(project_lang.motivation, project.motivation) as motivation,
  216. IFNULL(project_lang.video, project.video) as video,
  217. IFNULL(project_lang.about, project.about) as about,
  218. IFNULL(project_lang.goal, project.goal) as goal,
  219. IFNULL(project_lang.related, project.related) as related,
  220. IFNULL(project_lang.reward, project.reward) as reward,
  221. IFNULL(project_lang.keywords, project.keywords) as keywords,
  222. IFNULL(project_lang.media, project.media) as media,
  223. IFNULL(project_lang.subtitle, project.subtitle) as subtitle
  224. FROM project
  225. LEFT JOIN project_lang
  226. ON project_lang.id = project.id
  227. AND project_lang.lang = :lang
  228. WHERE project.id = :id
  229. ";
  230. $query = self::query($sql, array(':id'=>$id, ':lang'=>$lang));
  231. foreach ($query->fetch(\PDO::FETCH_ASSOC) as $field=>$value) {
  232. $project->$field = $value;
  233. }
  234. }
  235. if (isset($project->media)) {
  236. $project->media = new Project\Media($project->media);
  237. }
  238. if (isset($project->video)) {
  239. $project->video = new Project\Media($project->video);
  240. }
  241. // owner
  242. $project->user = User::get($project->owner, $lang);
  243. // galeria
  244. $project->gallery = Project\Image::getGallery($project->id);
  245. // imágenes por sección
  246. foreach (Project\Image::sections() as $sec => $val) {
  247. if ($sec != '') {
  248. $project->secGallery[$sec] = Project\Image::get($project->id, $sec);
  249. }
  250. }
  251. // categorias
  252. $project->categories = Project\Category::get($id);
  253. // costes y los sumammos
  254. $project->costs = Project\Cost::getAll($id, $lang);
  255. $project->minmax();
  256. // retornos colectivos
  257. $project->social_rewards = Project\Reward::getAll($id, 'social', $lang);
  258. // retornos individuales
  259. $project->individual_rewards = Project\Reward::getAll($id, 'individual', $lang);
  260. // colaboraciones
  261. $project->supports = Project\Support::getAll($id, $lang);
  262. //-----------------------------------------------------------------
  263. // Diferentes verificaciones segun el estado del proyecto
  264. //-----------------------------------------------------------------
  265. $project->investors = Invest::investors($id);
  266. $project->num_investors = Invest::numInvestors($id);
  267. $amount = Invest::invested($id);
  268. if ($project->invested != $amount) {
  269. self::query("UPDATE project SET amount = '{$amount}' WHERE id = ?", array($id));
  270. }
  271. $project->invested = $amount;
  272. $project->amount = $amount;
  273. //mensajes y mensajeros
  274. $messegers = array();
  275. $project->messages = Message::getAll($id, $lang);
  276. $project->num_messages = 0;
  277. foreach ($project->messages as $msg) {
  278. $project->num_messages++;
  279. $project->num_messages+=count($msg->responses);
  280. $messegers[$msg->user] = $msg->user;
  281. }
  282. $project->num_messegers = count($messegers);
  283. $project->setDays();
  284. $project->setTagmark();
  285. // fecha final primera ronda (fecha campaña + 40)
  286. if (!empty($project->published)) {
  287. $ptime = strtotime($project->published);
  288. $project->willpass = date('Y-m-d', \mktime(0, 0, 0, date('m', $ptime), date('d', $ptime)+40, date('Y', $ptime)));
  289. }
  290. //-----------------------------------------------------------------
  291. // Fin de verificaciones
  292. //-----------------------------------------------------------------
  293. return $project;
  294. } catch(\PDOException $e) {
  295. throw new \Goteo\Core\Exception($e->getMessage());
  296. } catch(\Goteo\Core\Error $e) {
  297. throw new \Goteo\Core\Error('404', Text::html('fatal-error-project'));
  298. }
  299. }
  300. /*
  301. * Cargamos los datos mínimos de un proyecto
  302. */
  303. public static function getMini($id) {
  304. try {
  305. // metemos los datos del proyecto en la instancia
  306. $query = self::query("SELECT id, name, owner, comment, lang, status FROM project WHERE id = ?", array($id));
  307. $project = $query->fetchObject(); // stdClass para qno grabar accidentalmente y machacar todo
  308. // owner
  309. $project->user = User::getMini($project->owner);
  310. return $project;
  311. } catch(\PDOException $e) {
  312. throw new \Goteo\Core\Exception($e->getMessage());
  313. }
  314. }
  315. /*
  316. * Cargamos los datos suficientes para pintar un widget de proyecto
  317. */
  318. public static function getMedium($id, $lang = \LANG) {
  319. try {
  320. // metemos los datos del proyecto en la instancia
  321. $query = self::query("SELECT * FROM project WHERE id = ?", array($id));
  322. $project = $query->fetchObject(__CLASS__);
  323. // primero, que no lo grabe
  324. $project->dontsave = true;
  325. // si recibimos lang y no es el idioma original del proyecto, ponemos la traducción y mantenemos para el resto de contenido
  326. if ($lang == $project->lang) {
  327. $lang = null;
  328. } elseif (!empty($lang)) {
  329. $sql = "
  330. SELECT
  331. IFNULL(project_lang.description, project.description) as description,
  332. IFNULL(project_lang.subtitle, project.subtitle) as subtitle
  333. FROM project
  334. LEFT JOIN project_lang
  335. ON project_lang.id = project.id
  336. AND project_lang.lang = :lang
  337. WHERE project.id = :id
  338. ";
  339. $query = self::query($sql, array(':id'=>$id, ':lang'=>$lang));
  340. foreach ($query->fetch(\PDO::FETCH_ASSOC) as $field=>$value) {
  341. $project->$field = $value;
  342. }
  343. }
  344. // owner
  345. $project->user = User::getMini($project->owner);
  346. // imagen
  347. $project->image = Project\Image::getFirst($project->id);
  348. // categorias
  349. $project->categories = Project\Category::getNames($id, 2);
  350. // retornos colectivos
  351. $project->social_rewards = Project\Reward::getAll($id, 'social', $lang);
  352. // retornos individuales
  353. $project->individual_rewards = Project\Reward::getAll($id, 'individual', $lang);
  354. $amount = Invest::invested($id);
  355. $project->invested = $amount;
  356. $project->amount = $amount;
  357. $project->num_investors = Invest::numInvestors($id);
  358. $project->num_messegers = Message::numMessegers($id);
  359. // sacamos rapidamente el presupuesto mínimo y óptimo
  360. $costs = self::calcCosts($id);
  361. $project->mincost = $costs->mincost;
  362. $project->maxcost = $costs->maxcost;
  363. $project->setDays();
  364. $project->setTagmark();
  365. return $project;
  366. } catch(\PDOException $e) {
  367. throw new \Goteo\Core\Exception($e->getMessage());
  368. }
  369. }
  370. /*
  371. * Listado simple de todos los proyectos
  372. */
  373. public static function getAll($node = \GOTEO_NODE) {
  374. $list = array();
  375. $query = static::query("
  376. SELECT
  377. project.id as id,
  378. project.name as name
  379. FROM project
  380. ORDER BY project.name ASC
  381. ", array(':node' => $node));
  382. foreach ($query->fetchAll(\PDO::FETCH_CLASS) as $item) {
  383. $list[$item->id] = $item->name;
  384. }
  385. return $list;
  386. }
  387. /*
  388. * Para calcular los dias y la ronda
  389. */
  390. private function setDays() {
  391. //para proyectos en campaña o posterior
  392. if ($this->status > 2) {
  393. // tiempo de campaña
  394. if ($this->status == 3) {
  395. $days = $this->daysActive();
  396. if ($days > 81) {
  397. $this->round = 0;
  398. $days = 0;
  399. } elseif ($days >= 40) {
  400. $days = 80 - $days;
  401. $this->round = 2;
  402. } else {
  403. $days = 40 - $days;
  404. $this->round = 1;
  405. }
  406. if ($days < 0) {
  407. // no deberia estar en campaña sino financuiado o caducado
  408. $days = 0;
  409. }
  410. } else {
  411. $this->round = 0;
  412. $days = 0;
  413. }
  414. } else {
  415. $days = 0;
  416. }
  417. if ($this->days != $days) {
  418. self::query("UPDATE project SET days = '{$days}' WHERE id = ?", array($this->id));
  419. }
  420. $this->days = $days;
  421. }
  422. /*
  423. * Para ver que tagmark le toca
  424. */
  425. private function setTagmark() {
  426. // a ver que banderolo le toca
  427. // "financiado" al final de de los 80 dias
  428. if ($this->status == 4) :
  429. $this->tagmark = 'gotit';
  430. // "en marcha" cuando llega al optimo en primera o segunda ronda
  431. elseif ($this->status == 3 && $this->amount >= $this->maxcost) :
  432. $this->tagmark = 'onrun';
  433. // "en marcha" y "aun puedes" cuando está en la segunda ronda
  434. elseif ($this->status == 3 && $this->round == 2) :
  435. $this->tagmark = 'onrun-keepiton';
  436. // Obtiene el mínimo durante la primera ronda, "aun puedes seguir aportando"
  437. elseif ($this->status == 3 && $this->round == 1 && $this->amount >= $this->mincost ) :
  438. $this->tagmark = 'keepiton';
  439. // tag de exitoso cuando es retorno cumplido
  440. elseif ($this->status == 5) :
  441. $this->tagmark = 'success';
  442. // tag de caducado
  443. elseif ($this->status == 6) :
  444. $this->tagmark = 'fail';
  445. endif;
  446. }
  447. /*
  448. * Para validar los campos del proyecto que son NOT NULL en la tabla
  449. */
  450. public function validate(&$errors = array()) {
  451. // Estos son errores que no permiten continuar
  452. if (empty($this->id))
  453. $errors[] = Text::_('El proyecto no tiene id');
  454. if (empty($this->lang))
  455. $this->lang = 'es';
  456. if (empty($this->status))
  457. $this->status = 1;
  458. if (empty($this->progress))
  459. $this->progress = 0;
  460. if (empty($this->owner))
  461. $errors[] = Text::_('El proyecto no tiene usuario creador');
  462. if (empty($this->node))
  463. $this->node = 'goteo';
  464. //cualquiera de estos errores hace fallar la validación
  465. if (!empty($errors))
  466. return false;
  467. else
  468. return true;
  469. }
  470. /**
  471. * actualiza en la tabla los datos del proyecto
  472. * @param array $project->errors para guardar los errores de datos del formulario, los errores de proceso se guardan en $project->errors['process']
  473. */
  474. public function save (&$errors = array()) {
  475. if ($this->dontsave) { return false; }
  476. if(!$this->validate($errors)) { return false; }
  477. try {
  478. // fail para pasar por todo antes de devolver false
  479. $fail = false;
  480. // los nif sin guiones, espacios ni puntos
  481. $this->contract_nif = str_replace(array('_', '.', ' ', '-', ',', ')', '('), '', $this->contract_nif);
  482. $this->entity_cif = str_replace(array('_', '.', ' ', '-', ',', ')', '('), '', $this->entity_cif);
  483. // Image
  484. if (is_array($this->image) && !empty($this->image['name'])) {
  485. $image = new Image($this->image);
  486. if ($image->save($errors)) {
  487. $this->gallery[] = $image;
  488. $this->image = $image->id;
  489. /**
  490. * Guarda la relación NM en la tabla 'project_image'.
  491. */
  492. if(!empty($image->id)) {
  493. self::query("REPLACE project_image (project, image) VALUES (:project, :image)", array(':project' => $this->id, ':image' => $image->id));
  494. }
  495. }
  496. }
  497. $fields = array(
  498. 'contract_name',
  499. 'contract_nif',
  500. 'contract_email',
  501. 'contract_entity',
  502. 'contract_birthdate',
  503. 'entity_office',
  504. 'entity_name',
  505. 'entity_cif',
  506. 'phone',
  507. 'address',
  508. 'zipcode',
  509. 'location',
  510. 'country',
  511. 'secondary_address',
  512. 'post_address',
  513. 'post_zipcode',
  514. 'post_location',
  515. 'post_country',
  516. 'name',
  517. 'subtitle',
  518. 'image',
  519. 'description',
  520. 'motivation',
  521. 'video',
  522. 'video_usubs',
  523. 'about',
  524. 'goal',
  525. 'related',
  526. 'reward',
  527. 'keywords',
  528. 'media',
  529. 'media_usubs',
  530. 'currently',
  531. 'project_location',
  532. 'scope',
  533. 'resource',
  534. 'comment'
  535. );
  536. $set = '';
  537. $values = array();
  538. foreach ($fields as $field) {
  539. if ($set != '') $set .= ', ';
  540. $set .= "$field = :$field";
  541. $values[":$field"] = $this->$field;
  542. }
  543. // Solamente marcamos updated cuando se envia a revision desde el superform o el admin
  544. // $set .= ", updated = :updated";
  545. // $values[':updated'] = date('Y-m-d');
  546. $values[':id'] = $this->id;
  547. $sql = "UPDATE project SET " . $set . " WHERE id = :id";
  548. if (!self::query($sql, $values)) {
  549. $errors[] = $sql . '<pre>' . print_r($values, 1) . '</pre>';
  550. $fail = true;
  551. }
  552. // echo "$sql<br />";
  553. // y aquí todas las tablas relacionadas
  554. // cada una con sus save, sus new y sus remove
  555. // quitar las que tiene y no vienen
  556. // añadir las que vienen y no tiene
  557. //categorias
  558. $tiene = Project\Category::get($this->id);
  559. $viene = $this->categories;
  560. $quita = array_diff_assoc($tiene, $viene);
  561. $guarda = array_diff_assoc($viene, $tiene);
  562. foreach ($quita as $key=>$item) {
  563. $category = new Project\Category(
  564. array(
  565. 'id'=>$item,
  566. 'project'=>$this->id)
  567. );
  568. if (!$category->remove($errors))
  569. $fail = true;
  570. }
  571. foreach ($guarda as $key=>$item) {
  572. if (!$item->save($errors))
  573. $fail = true;
  574. }
  575. // recuperamos las que le quedan si ha cambiado alguna
  576. if (!empty($quita) || !empty($guarda))
  577. $this->categories = Project\Category::get($this->id);
  578. //costes
  579. $tiene = Project\Cost::getAll($this->id);
  580. $viene = $this->costs;
  581. $quita = array_diff_key($tiene, $viene);
  582. $guarda = array_diff_key($viene, $tiene);
  583. foreach ($quita as $key=>$item) {
  584. if (!$item->remove($errors)) {
  585. $fail = true;
  586. } else {
  587. unset($tiene[$key]);
  588. }
  589. }
  590. foreach ($guarda as $key=>$item) {
  591. if (!$item->save($errors))
  592. $fail = true;
  593. }
  594. /* Ahora, los que tiene y vienen. Si el contenido es diferente, hay que guardarlo*/
  595. foreach ($tiene as $key => $row) {
  596. // a ver la diferencia con el que viene
  597. if ($row != $viene[$key]) {
  598. if (!$viene[$key]->save($errors))
  599. $fail = true;
  600. }
  601. }
  602. if (!empty($quita) || !empty($guarda))
  603. $this->costs = Project\Cost::getAll($this->id);
  604. // recalculo de minmax
  605. $this->minmax();
  606. //retornos colectivos
  607. $tiene = Project\Reward::getAll($this->id, 'social');
  608. $viene = $this->social_rewards;
  609. $quita = array_diff_key($tiene, $viene);
  610. $guarda = array_diff_key($viene, $tiene);
  611. foreach ($quita as $key=>$item) {
  612. if (!$item->remove($errors)) {
  613. $fail = true;
  614. } else {
  615. unset($tiene[$key]);
  616. }
  617. }
  618. foreach ($guarda as $key=>$item) {
  619. if (!$item->save($errors))
  620. $fail = true;
  621. }
  622. /* Ahora, los que tiene y vienen. Si el contenido es diferente, hay que guardarlo*/
  623. foreach ($tiene as $key => $row) {
  624. // a ver la diferencia con el que viene
  625. if ($row != $viene[$key]) {
  626. if (!$viene[$key]->save($errors))
  627. $fail = true;
  628. }
  629. }
  630. if (!empty($quita) || !empty($guarda))
  631. $this->social_rewards = Project\Reward::getAll($this->id, 'social');
  632. //recompenssas individuales
  633. $tiene = Project\Reward::getAll($this->id, 'individual');
  634. $viene = $this->individual_rewards;
  635. $quita = array_diff_key($tiene, $viene);
  636. $guarda = array_diff_key($viene, $tiene);
  637. foreach ($quita as $key=>$item) {
  638. if (!$item->remove($errors)) {
  639. $fail = true;
  640. } else {
  641. unset($tiene[$key]);
  642. }
  643. }
  644. foreach ($guarda as $key=>$item) {
  645. if (!$item->save($errors))
  646. $fail = true;
  647. }
  648. /* Ahora, los que tiene y vienen. Si el contenido es diferente, hay que guardarlo*/
  649. foreach ($tiene as $key => $row) {
  650. // a ver la diferencia con el que viene
  651. if ($row != $viene[$key]) {
  652. if (!$viene[$key]->save($errors))
  653. $fail = true;
  654. }
  655. }
  656. if (!empty($quita) || !empty($guarda))
  657. $this->individual_rewards = Project\Reward::getAll($this->id, 'individual');
  658. // colaboraciones
  659. $tiene = Project\Support::getAll($this->id);
  660. $viene = $this->supports;
  661. $quita = array_diff_key($tiene, $viene); // quitar los que tiene y no viene
  662. $guarda = array_diff_key($viene, $tiene); // añadir los que viene y no tiene
  663. foreach ($quita as $key=>$item) {
  664. if (!$item->remove($errors)) {
  665. $fail = true;
  666. } else {
  667. unset($tiene[$key]);
  668. }
  669. }
  670. foreach ($guarda as $key=>$item) {
  671. if (!$item->save($errors))
  672. $fail = true;
  673. }
  674. /* Ahora, los que tiene y vienen. Si el contenido es diferente, hay que guardarlo*/
  675. foreach ($tiene as $key => $row) {
  676. // a ver la diferencia con el que viene
  677. if ($row != $viene[$key]) {
  678. if (!$viene[$key]->save($errors))
  679. $fail = true;
  680. }
  681. }
  682. if (!empty($quita) || !empty($guarda))
  683. $this->supports = Project\Support::getAll($this->id);
  684. //listo
  685. return !$fail;
  686. } catch(\PDOException $e) {
  687. $errors[] = Text::_('No se ha grabado correctamente. ') . $e->getMessage();
  688. //Text::get('save-project-fail');
  689. return false;
  690. }
  691. }
  692. public function saveLang (&$errors = array()) {
  693. try {
  694. $fields = array(
  695. 'id'=>'id',
  696. 'lang'=>'lang_lang',
  697. 'subtitle'=>'subtitle_lang',
  698. 'description'=>'description_lang',
  699. 'motivation'=>'motivation_lang',
  700. 'video'=>'video_lang',
  701. 'about'=>'about_lang',
  702. 'goal'=>'goal_lang',
  703. 'related'=>'related_lang',
  704. 'reward'=>'reward_lang',
  705. 'keywords'=>'keywords_lang',
  706. 'media'=>'media_lang'
  707. );
  708. $set = '';
  709. $values = array();
  710. foreach ($fields as $field=>$ffield) {
  711. if ($set != '') $set .= ', ';
  712. $set .= "$field = :$field";
  713. if (empty($this->$ffield)) {
  714. $this->$ffield = null;
  715. }
  716. $values[":$field"] = $this->$ffield;
  717. }
  718. $sql = "REPLACE INTO project_lang SET " . $set;
  719. if (self::query($sql, $values)) {
  720. return true;
  721. } else {
  722. $errors[] = $sql . '<pre>' . print_r($values, 1) . '</pre>';
  723. return false;
  724. }
  725. } catch(\PDOException $e) {
  726. $errors[] = Text::_('No se ha grabado correctamente. ') . $e->getMessage();
  727. return false;
  728. }
  729. }
  730. /*
  731. * comprueba errores de datos y actualiza la puntuación
  732. */
  733. public function check() {
  734. $errors = &$this->errors;
  735. $okeys = &$this->okeys ;
  736. // reseteamos la puntuación
  737. $this->setScore(0, 0, true);
  738. /***************** Revisión de campos del paso 1, PERFIL *****************/
  739. $score = 0;
  740. // obligatorios: nombre, email, ciudad
  741. if (empty($this->user->name)) {
  742. $errors['userProfile']['name'] = Text::get('validate-user-field-name');
  743. } else {
  744. $okeys['userProfile']['name'] = 'ok';
  745. ++$score;
  746. }
  747. // se supone que tiene email porque sino no puede tener usuario, no?
  748. if (!empty($this->user->email)) {
  749. ++$score;
  750. }
  751. if (empty($this->user->location)) {
  752. $errors['userProfile']['location'] = Text::get('validate-user-field-location');
  753. } else {
  754. $okeys['userProfile']['location'] = 'ok';
  755. ++$score;
  756. }
  757. if(!empty($this->user->avatar) && $this->user->avatar->id != 1) {
  758. $okeys['userProfile']['avatar'] = empty($errors['userProfile']['avatar']) ? 'ok' : null;
  759. $score+=2;
  760. }
  761. if (!empty($this->user->about)) {
  762. $okeys['userProfile']['about'] = 'ok';
  763. ++$score;
  764. // otro +1 si tiene más de 1000 caracteres (pero menos de 2000)
  765. if (\strlen($this->user->about) > 1000 && \strlen($this->user->about) < 2000) {
  766. ++$score;
  767. }
  768. } else {
  769. $errors['userProfile']['about'] = Text::get('validate-user-field-about');
  770. }
  771. if (!empty($this->user->interests)) {
  772. $okeys['userProfile']['interests'] = 'ok';
  773. ++$score;
  774. }
  775. /* Aligerando superform
  776. if (!empty($this->user->keywords)) {
  777. $okeys['userProfile']['keywords'] = 'ok';
  778. ++$score;
  779. }
  780. if (!empty($this->user->contribution)) {
  781. $okeys['userProfile']['contribution'] = 'ok';
  782. ++$score;
  783. }
  784. */
  785. if (empty($this->user->webs)) {
  786. $errors['userProfile']['webs'] = Text::get('validate-project-userProfile-web');
  787. } else {
  788. $okeys['userProfile']['webs'] = 'ok';
  789. ++$score;
  790. if (count($this->user->webs) > 2) ++$score;
  791. $anyerror = false;
  792. foreach ($this->user->webs as $web) {
  793. if (trim(str_replace('http://','',$web->url)) == '') {
  794. $anyerror = !$anyerror ?: true;
  795. $errors['userProfile']['web-'.$web->id.'-url'] = Text::get('validate-user-field-web');
  796. } else {
  797. $okeys['userProfile']['web-'.$web->id.'-url'] = 'ok';
  798. }
  799. }
  800. if ($anyerror) {
  801. unset($okeys['userProfile']['webs']);
  802. $errors['userProfile']['webs'] = Text::get('validate-project-userProfile-any_error');
  803. }
  804. }
  805. if (!empty($this->user->facebook)) {
  806. $okeys['userProfile']['facebook'] = 'ok';
  807. ++$score;
  808. }
  809. if (!empty($this->user->twitter)) {
  810. $okeys['userProfile']['twitter'] = 'ok';
  811. ++$score;
  812. }
  813. if (!empty($this->user->linkedin)) {
  814. $okeys['userProfile']['linkedin'] = 'ok';
  815. }
  816. //puntos
  817. $this->setScore($score, 12);
  818. /***************** FIN Revisión del paso 1, PERFIL *****************/
  819. /***************** Revisión de campos del paso 2,DATOS PERSONALES *****************/
  820. $score = 0;
  821. // obligatorios: todos
  822. if (empty($this->contract_name)) {
  823. $errors['userPersonal']['contract_name'] = Text::get('mandatory-project-field-contract_name');
  824. } else {
  825. $okeys['userPersonal']['contract_name'] = 'ok';
  826. ++$score;
  827. }
  828. if (empty($this->contract_nif)) {
  829. $errors['userPersonal']['contract_nif'] = Text::get('mandatory-project-field-contract_nif');
  830. } elseif (!Check::nif($this->contract_nif) && !Check::vat($this->contract_nif)) {
  831. $errors['userPersonal']['contract_nif'] = Text::get('validate-project-value-contract_nif');
  832. } else {
  833. $okeys['userPersonal']['contract_nif'] = 'ok';
  834. ++$score;
  835. }
  836. if (empty($this->contract_email)) {
  837. $errors['userPersonal']['contract_email'] = Text::get('mandatory-project-field-contract_email');
  838. } elseif (!Check::mail($this->contract_email)) {
  839. $errors['userPersonal']['contract_email'] = Text::get('validate-project-value-contract_email');
  840. } else {
  841. $okeys['userPersonal']['contract_email'] = 'ok';
  842. }
  843. if (empty($this->contract_birthdate)) {
  844. $errors['userPersonal']['contract_birthdate'] = Text::get('mandatory-project-field-contract_birthdate');
  845. } else {
  846. $okeys['userPersonal']['contract_birthdate'] = 'ok';
  847. }
  848. if (empty($this->phone)) {
  849. $errors['userPersonal']['phone'] = Text::get('mandatory-project-field-phone');
  850. } elseif (!Check::phone($this->phone)) {
  851. $errors['userPersonal']['phone'] = Text::get('validate-project-value-phone');
  852. } else {
  853. $okeys['userPersonal']['phone'] = 'ok';
  854. ++$score;
  855. }
  856. if (empty($this->address)) {
  857. $errors['userPersonal']['address'] = Text::get('mandatory-project-field-address');
  858. } else {
  859. $okeys['userPersonal']['address'] = 'ok';
  860. ++$score;
  861. }
  862. if (empty($this->zipcode)) {
  863. $errors['userPersonal']['zipcode'] = Text::get('mandatory-project-field-zipcode');
  864. } else {
  865. $okeys['userPersonal']['zipcode'] = 'ok';
  866. ++$score;
  867. }
  868. if (empty($this->location)) {
  869. $errors['userPersonal']['location'] = Text::get('mandatory-project-field-residence');
  870. } else {
  871. $okeys['userPersonal']['location'] = 'ok';
  872. }
  873. if (empty($this->country)) {
  874. $errors['userPersonal']['country'] = Text::get('mandatory-project-field-country');
  875. } else {
  876. $okeys['userPersonal']['country'] = 'ok';
  877. ++$score;
  878. }
  879. $this->setScore($score, 6);
  880. /***************** FIN Revisión del paso 2, DATOS PERSONALES *****************/
  881. /***************** Revisión de campos del paso 3, DESCRIPCION *****************/
  882. $score = 0;
  883. // obligatorios: nombre, subtitulo, imagen, descripcion, about, motivation, categorias, video, localización
  884. if (empty($this->name)) {
  885. $errors['overview']['name'] = Text::get('mandatory-project-field-name');
  886. } else {
  887. $okeys['overview']['name'] = 'ok';
  888. ++$score;
  889. }
  890. if (!empty($this->subtitle)) {
  891. $okeys['overview']['subtitle'] = 'ok';
  892. }
  893. if (empty($this->gallery) && empty($errors['overview']['image'])) {
  894. $errors['overview']['image'] .= Text::get('mandatory-project-field-image');
  895. } else {
  896. $okeys['overview']['image'] = (empty($errors['overview']['image'])) ? 'ok' : null;
  897. ++$score;
  898. if (count($this->gallery) >= 2) ++$score;
  899. }
  900. if (empty($this->description)) {
  901. $errors['overview']['description'] = Text::get('mandatory-project-field-description');
  902. } elseif (!Check::words($this->description, 80)) {
  903. $errors['overview']['description'] = Text::get('validate-project-field-description');
  904. } else {
  905. $okeys['overview']['description'] = 'ok';
  906. ++$score;
  907. }
  908. if (empty($this->about)) {
  909. $errors['overview']['about'] = Text::get('mandatory-project-field-about');
  910. } else {
  911. $okeys['overview']['about'] = 'ok';
  912. ++$score;
  913. }
  914. if (empty($this->motivation)) {
  915. $errors['overview']['motivation'] = Text::get('mandatory-project-field-motivation');
  916. } else {
  917. $okeys['overview']['motivation'] = 'ok';
  918. ++$score;
  919. }
  920. if (!empty($this->goal)) {
  921. $okeys['overview']['goal'] = 'ok';
  922. ++$score;
  923. }
  924. if (!empty($this->related)) {
  925. $okeys['overview']['related'] = 'ok';
  926. ++$score;
  927. }
  928. if (empty($this->categories)) {
  929. $errors['overview']['categories'] = Text::get('mandatory-project-field-category');
  930. } else {
  931. $okeys['overview']['categories'] = 'ok';
  932. ++$score;
  933. }
  934. if (empty($this->media)) {
  935. $errors['overview']['media'] = Text::get('mandatory-project-field-media');
  936. } else {
  937. $okeys['overview']['media'] = 'ok';
  938. $score+=3;
  939. }
  940. if (empty($this->project_location)) {
  941. $errors['overview']['project_location'] = Text::get('mandatory-project-field-location');
  942. } else {
  943. $okeys['overview']['project_location'] = 'ok';
  944. ++$score;
  945. }
  946. $this->setScore($score, 13);
  947. /***************** FIN Revisión del paso 3, DESCRIPCION *****************/
  948. /***************** Revisión de campos del paso 4, COSTES *****************/
  949. $score = 0; $scoreName = $scoreDesc = $scoreAmount = $scoreDate = 0;
  950. if (count($this->costs) < 2) {
  951. $errors['costs']['costs'] = Text::get('mandatory-project-costs');
  952. } else {
  953. $okeys['costs']['costs'] = 'ok';
  954. ++$score;
  955. }
  956. $anyerror = false;
  957. foreach($this->costs as $cost) {
  958. if (empty($cost->cost)) {
  959. $errors['costs']['cost-'.$cost->id.'-cost'] = Text::get('mandatory-cost-field-name');
  960. $anyerror = !$anyerror ?: true;
  961. } else {
  962. $okeys['costs']['cost-'.$cost->id.'-cost'] = 'ok';
  963. $scoreName = 1;
  964. }
  965. if (empty($cost->type)) {
  966. $errors['costs']['cost-'.$cost->id.'-type'] = Text::get('mandatory-cost-field-type');
  967. $anyerror = !$anyerror ?: true;
  968. } else {
  969. $okeys['costs']['cost-'.$cost->id.'-type'] = 'ok';
  970. }
  971. if (empty($cost->description)) {
  972. $errors['costs']['cost-'.$cost->id.'-description'] = Text::get('mandatory-cost-field-description');
  973. $anyerror = !$anyerror ?: true;
  974. } else {
  975. $okeys['costs']['cost-'.$cost->id.'-description'] = 'ok';
  976. $scoreDesc = 1;
  977. }
  978. if (empty($cost->amount)) {
  979. $errors['costs']['cost-'.$cost->id.'-amount'] = Text::get('mandatory-cost-field-amount');
  980. $anyerror = !$anyerror ?: true;
  981. } else {
  982. $okeys['costs']['cost-'.$cost->id.'-amount'] = 'ok';
  983. $scoreAmount = 1;
  984. }
  985. if ($cost->type == 'task' && (empty($cost->from) || empty($cost->until))) {
  986. $errors['costs']['cost-'.$cost->id.'-dates'] = Text::get('mandatory-cost-field-task_dates');
  987. $anyerror = !$anyerror ?: true;
  988. } elseif ($cost->type == 'task') {
  989. $okeys['costs']['cost-'.$cost->id.'-dates'] = 'ok';
  990. $scoreDate = 1;
  991. }
  992. }
  993. if ($anyerror) {
  994. unset($okeys['costs']['costs']);
  995. $errors['costs']['costs'] = Text::get('validate-project-costs-any_error');
  996. }
  997. $score = $score + $scoreName + $scoreDesc + $scoreAmount + $scoreDate;
  998. $costdif = $this->maxcost - $this->mincost;
  999. $maxdif = $this->mincost * 0.50;
  1000. $scoredif = $this->mincost * 0.35;
  1001. if ($this->mincost == 0) {
  1002. $errors['costs']['total-costs'] = Text::get('mandatory-project-total-costs');
  1003. } elseif ($costdif > $maxdif ) {
  1004. $errors['costs']['total-costs'] = Text::get('validate-project-total-costs');
  1005. } else {
  1006. $okeys['costs']['total-costs'] = 'ok';
  1007. }
  1008. if ($costdif <= $scoredif ) {
  1009. ++$score;
  1010. }
  1011. $this->setScore($score, 6);
  1012. /***************** FIN Revisión del paso 4, COSTES *****************/
  1013. /***************** Revisión de campos del paso 5, RETORNOS *****************/
  1014. $score = 0; $scoreName = $scoreDesc = $scoreAmount = $scoreLicense = 0;
  1015. if (empty($this->social_rewards)) {
  1016. $errors['rewards']['social_rewards'] = Text::get('validate-project-social_rewards');
  1017. } else {
  1018. $okeys['rewards']['social_rewards'] = 'ok';
  1019. if (count($this->social_rewards) >= 2) {
  1020. ++$score;
  1021. }
  1022. }
  1023. if (empty($this->individual_rewards)) {
  1024. $errors['rewards']['individual_rewards'] = Text::get('validate-project-individual_rewards');
  1025. } else {
  1026. $okeys['rewards']['individual_rewards'] = 'ok';
  1027. if (count($this->individual_rewards) >= 3) {
  1028. ++$score;
  1029. }
  1030. }
  1031. $anyerror = false;
  1032. foreach ($this->social_rewards as $social) {
  1033. if (empty($social->reward)) {
  1034. $errors['rewards']['social_reward-'.$social->id.'reward'] = Text::get('mandatory-social_reward-field-name');
  1035. $anyerror = !$anyerror ?: true;
  1036. } else {
  1037. $okeys['rewards']['social_reward-'.$social->id.'reward'] = 'ok';
  1038. $scoreName = 1;
  1039. }
  1040. if (empty($social->description)) {
  1041. $errors['rewards']['social_reward-'.$social->id.'-description'] = Text::get('mandatory-social_reward-field-description');
  1042. $anyerror = !$anyerror ?: true;
  1043. } else {
  1044. $okeys['rewards']['social_reward-'.$social->id.'-description'] = 'ok';
  1045. $scoreDesc = 1;
  1046. }
  1047. if (empty($social->icon)) {
  1048. $errors['rewards']['social_reward-'.$social->id.'-icon'] = Text::get('mandatory-social_reward-field-icon');
  1049. $anyerror = !$anyerror ?: true;
  1050. } else {
  1051. $okeys['rewards']['social_reward-'.$social->id.'-icon'] = 'ok';
  1052. }
  1053. if (!empty($social->license)) {
  1054. $scoreLicense = 1;
  1055. }
  1056. }
  1057. if ($anyerror) {
  1058. unset($okeys['rewards']['social_rewards']);
  1059. $errors['rewards']['social_rewards'] = Text::get('validate-project-social_rewards-any_error');
  1060. }
  1061. $score = $score + $scoreName + $scoreDesc + $scoreLicense;
  1062. $scoreName = $scoreDesc = $scoreAmount = 0;
  1063. $anyerror = false;
  1064. foreach ($this->individual_rewards as $individual) {
  1065. if (empty($individual->reward)) {
  1066. $errors['rewards']['individual_reward-'.$individual->id.'-reward'] = Text::get('mandatory-individual_reward-field-name');
  1067. $anyerror = !$anyerror ?: true;
  1068. } else {
  1069. $okeys['rewards']['individual_reward-'.$individual->id.'-reward'] = 'ok';
  1070. $scoreName = 1;
  1071. }
  1072. if (empty($individual->description)) {
  1073. $errors['rewards']['individual_reward-'.$individual->id.'-descript…

Large files files are truncated, but you can click here to view the full file