From 62ccceab69c021eaa76cd4b91bcc997482ab7d17 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 8 May 2023 13:13:52 +0200 Subject: [PATCH 1/2] Create Permanent Campaigns (#542) * add project conf type migration * add type to Project Conf model * add type to project conf form * add save to type field in project campaign form * add test for new layout with permanent campaigns * add type_of_campaign to commands dealing with projects * add new widget partials for permanent campaigns * add calls in ProjectConf and Project to know if a project is permanent or campaign type * fixes in widget normal_permanent partial * add permanent filter in home goteo * add permanent filter in discover * remove test visualization * improve message in round command regarding permanent campaigns * add missing copies --- .../channel/partials/projects_block.php | 15 +- .../channel/partials/projects_list.php | 12 +- .../partials/projects_widgets_list.php | 16 ++- .../responsive/dashboard/project/campaign.php | 8 ++ .../discover/partials/projects_list.php | 15 +- .../discover/partials/projects_nav.php | 5 + .../home/partials/projects_list.php | 8 +- .../responsive/home/partials/projects_nav.php | 3 + .../widgets/partials/data_list_permanent.php | 19 +++ .../templates/responsive/project/layout.php | 23 +-- .../project/partials/main_extra.php | 4 +- .../responsive/project/partials/media.php | 2 +- .../responsive/project/partials/meter.php | 111 ++++++-------- .../project/partials/related_projects.php | 19 +-- .../project/widgets/normal_permanent.php | 85 +++++++++++ .../widgets/partials/backside_call.php | 6 +- .../widgets/partials/backside_normal.php | 6 +- .../widgets/partials/data_list_permanent.php | 19 +++ .../templates/responsive/user/profile.php | 24 ++-- .../templates/responsive/widget/project.php | 6 +- .../20230324132727_goteo_project_add_type.php | 51 +++++++ public/assets/sass/components/_widgets.scss | 5 + .../Console/Command/ProjectWatcherCommand.php | 35 +++-- src/Goteo/Console/Command/RoundCommand.php | 11 +- src/Goteo/Controller/ChannelController.php | 5 +- src/Goteo/Controller/DiscoverController.php | 20 ++- src/Goteo/Controller/ProjectController.php | 135 +++++++++--------- .../Forms/Model/ProjectCampaignForm.php | 103 ++++++++----- src/Goteo/Model/Project.php | 38 ++++- src/Goteo/Model/Project/Conf.php | 34 ++++- translations/ca/discover.yml | 1 + translations/ca/project.yml | 3 + translations/en/discover.yml | 1 + translations/en/project.yml | 4 +- translations/es/discover.yml | 1 + translations/es/project.yml | 3 + 36 files changed, 607 insertions(+), 249 deletions(-) create mode 100644 Resources/templates/responsive/partials/components/widgets/partials/data_list_permanent.php create mode 100644 Resources/templates/responsive/project/widgets/normal_permanent.php create mode 100644 Resources/templates/responsive/project/widgets/partials/data_list_permanent.php create mode 100644 db/migrations/20230324132727_goteo_project_add_type.php diff --git a/Resources/templates/responsive/channel/partials/projects_block.php b/Resources/templates/responsive/channel/partials/projects_block.php index fbc36b7e31..9f40967d44 100644 --- a/Resources/templates/responsive/channel/partials/projects_block.php +++ b/Resources/templates/responsive/channel/partials/projects_block.php @@ -2,10 +2,17 @@
projects as $project): ?>
- insert('project/widgets/normal', [ - 'project' => $project, - 'admin' => (bool)$this->admin - ]) ?> + isPermanent()): ?> + insert('project/widgets/normal_permanent', [ + 'project' => $project, + 'admin' => (bool)$this->admin + ]) ?> + + insert('project/widgets/normal', [ + 'project' => $project, + 'admin' => (bool)$this->admin + ]) ?> +
diff --git a/Resources/templates/responsive/channel/partials/projects_list.php b/Resources/templates/responsive/channel/partials/projects_list.php index 9c4d96e186..965d227c68 100644 --- a/Resources/templates/responsive/channel/partials/projects_list.php +++ b/Resources/templates/responsive/channel/partials/projects_list.php @@ -1,9 +1,11 @@
projects as $project) : ?> -
- insert('project/widgets/normal', [ - 'project' => $project - ]) ?> -
+
+ isPermanent()): ?> + insert('project/widgets/normal_permanent', ['project' => $project]) ?> + + insert('project/widgets/normal', ['project' => $project]) ?> + +
diff --git a/Resources/templates/responsive/dashboard/partials/projects_widgets_list.php b/Resources/templates/responsive/dashboard/partials/projects_widgets_list.php index c5c0fd4c78..584ab1adc8 100644 --- a/Resources/templates/responsive/dashboard/partials/projects_widgets_list.php +++ b/Resources/templates/responsive/dashboard/partials/projects_widgets_list.php @@ -1,11 +1,17 @@ projects): ?> projects as $project) : ?>
- insert('project/widgets/normal', [ - 'project' => $project, - // 'admin' => $project->userCanEdit($this->get_user()) - 'admin' => (bool)$this->admin - ]) ?> + isPermanent()): ?> + insert('project/widgets/normal_permanent', [ + 'project' => $project, + 'admin' => (bool)$this->admin + ]) ?> + + insert('project/widgets/normal', [ + 'project' => $project, + 'admin' => (bool)$this->admin + ]) ?> +
diff --git a/Resources/templates/responsive/dashboard/project/campaign.php b/Resources/templates/responsive/dashboard/project/campaign.php index ef41478d1f..0a6a0c05de 100644 --- a/Resources/templates/responsive/dashboard/project/campaign.php +++ b/Resources/templates/responsive/dashboard/project/campaign.php @@ -26,6 +26,14 @@ $(function(){ + $('#autoform_type').on('change', function() { + const value = this.value; + if (value == 'permanent') + $('#form-one_round').hide(); + else + $('#form-one_round').show(); + }) + $('#autoform_one_round input[type="radio"]').on('change', function() { var $help = $(this).closest('.input-wrap').find('.help-text'); $active = $help.find('span').eq(1-$(this).val()).removeClass('hidden'); diff --git a/Resources/templates/responsive/discover/partials/projects_list.php b/Resources/templates/responsive/discover/partials/projects_list.php index 22c76d48f0..af47947098 100644 --- a/Resources/templates/responsive/discover/partials/projects_list.php +++ b/Resources/templates/responsive/discover/partials/projects_list.php @@ -4,10 +4,17 @@ projects) : ?> projects as $project): ?>
- insert('project/widgets/normal', [ - 'project' => $project, - 'admin' => (bool)$this->admin - ]) ?> + isPermanent()): ?> + insert('project/widgets/normal_permanent', [ + 'project' => $project, + 'admin' => (bool)$this->admin + ]) ?> + + insert('project/widgets/normal', [ + 'project' => $project, + 'admin' => (bool)$this->admin + ]) ?> +
diff --git a/Resources/templates/responsive/discover/partials/projects_nav.php b/Resources/templates/responsive/discover/partials/projects_nav.php index adbefd2850..bc2d2be6a7 100644 --- a/Resources/templates/responsive/discover/partials/projects_nav.php +++ b/Resources/templates/responsive/discover/partials/projects_nav.php @@ -34,4 +34,9 @@ text('discover-group-archive-header') ?> stop() ?> + section('project-filters-item-7') ?> +
  • + text('discover-group-permanent-header') ?> +
  • + stop() ?> diff --git a/Resources/templates/responsive/home/partials/projects_list.php b/Resources/templates/responsive/home/partials/projects_list.php index 9c4d96e186..9bdad7a521 100644 --- a/Resources/templates/responsive/home/partials/projects_list.php +++ b/Resources/templates/responsive/home/partials/projects_list.php @@ -1,9 +1,11 @@
    projects as $project) : ?>
    - insert('project/widgets/normal', [ - 'project' => $project - ]) ?> + isPermanent()): ?> + insert('project/widgets/normal_permanent', ['project' => $project]) ?> + + insert('project/widgets/normal', ['project' => $project]) ?> +
    diff --git a/Resources/templates/responsive/home/partials/projects_nav.php b/Resources/templates/responsive/home/partials/projects_nav.php index 5d6473663d..60566f6da8 100644 --- a/Resources/templates/responsive/home/partials/projects_nav.php +++ b/Resources/templates/responsive/home/partials/projects_nav.php @@ -11,4 +11,7 @@
  • text('discover-group-recent-header') ?>
  • +
  • + text('discover-group-permanent-header') ?> +
  • diff --git a/Resources/templates/responsive/partials/components/widgets/partials/data_list_permanent.php b/Resources/templates/responsive/partials/components/widgets/partials/data_list_permanent.php new file mode 100644 index 0000000000..5e97fac5f8 --- /dev/null +++ b/Resources/templates/responsive/partials/components/widgets/partials/data_list_permanent.php @@ -0,0 +1,19 @@ + diff --git a/Resources/templates/responsive/project/layout.php b/Resources/templates/responsive/project/layout.php index 5842b11bf5..948d651315 100644 --- a/Resources/templates/responsive/project/layout.php +++ b/Resources/templates/responsive/project/layout.php @@ -69,14 +69,14 @@
    user->name ?>
    -
    -
    - insert('project/partials/media', ['project' => $project ]) ?> -
    -
    - insert('project/partials/meter', ['project' => $project ]) ?> -
    -
    +
    +
    + insert('project/partials/media', ['project' => $project ]) ?> +
    +
    + insert('project/partials/meter', ['project' => $project ]) ?> +
    +
    @@ -95,6 +95,13 @@
    + + isPermanent()):?> +
    + insert('project/partials/highlighted_rewards') ?> +
    + +
    supply('main-content') ?>
    diff --git a/Resources/templates/responsive/project/partials/main_extra.php b/Resources/templates/responsive/project/partials/main_extra.php index d9216aa833..9d56eb352b 100644 --- a/Resources/templates/responsive/project/partials/main_extra.php +++ b/Resources/templates/responsive/project/partials/main_extra.php @@ -134,7 +134,9 @@ - insert('project/partials/responsive_meter.php', ['project' => $project ]) ?> + type = 'campaign'): ?> + insert('project/partials/responsive_meter.php', ['project' => $project ]) ?> + - insert('project/widgets/partials/data_list') ?> + project->isPermanent()): ?> + insert('project/widgets/partials/data_list_permanent', ['project' => $this->project]) ?> + + insert('project/widgets/partials/data_list') ?> + project->inCampaign()): ?>
    diff --git a/Resources/templates/responsive/project/widgets/partials/backside_normal.php b/Resources/templates/responsive/project/widgets/partials/backside_normal.php index 0d0614e322..a2df216573 100644 --- a/Resources/templates/responsive/project/widgets/partials/backside_normal.php +++ b/Resources/templates/responsive/project/widgets/partials/backside_normal.php @@ -1,7 +1,11 @@
    - insert('project/widgets/partials/data_list') ?> + project->isPermanent()): ?> + insert('project/widgets/partials/data_list_permanent', ['project' => $this->project]) ?> + + insert('project/widgets/partials/data_list') ?> + project->inCampaign()): ?>
    diff --git a/Resources/templates/responsive/project/widgets/partials/data_list_permanent.php b/Resources/templates/responsive/project/widgets/partials/data_list_permanent.php new file mode 100644 index 0000000000..5e97fac5f8 --- /dev/null +++ b/Resources/templates/responsive/project/widgets/partials/data_list_permanent.php @@ -0,0 +1,19 @@ +
      +
    • +
      text('project-obtained') ?>
      +

      project->amount) ?>

      +
    • +
    • +
    • +
      project->num_investors . ' ' . $this->text('project-menu-supporters') ?>
      +
    • + project->project_location): + // TODO: link this to some map? + ?> +
    • +
    • +
      project->project_location ?>
      +
    • + +
    diff --git a/Resources/templates/responsive/user/profile.php b/Resources/templates/responsive/user/profile.php index 597700f989..eb7971991c 100644 --- a/Resources/templates/responsive/user/profile.php +++ b/Resources/templates/responsive/user/profile.php @@ -223,11 +223,13 @@
    my_projects as $project) : ?> -
    - insert('project/widgets/normal', [ - 'project' => $project - ]) ?> -
    +
    + isPermanent()): ?> + insert('project/widgets/normal_permanent', ['project' => $project]) ?> + + insert('project/widgets/normal', ['project' => $project]) ?> + +
    @@ -245,11 +247,13 @@
    invest_on as $project) : ?> -
    - insert('project/widgets/normal', [ - 'project' => $project - ]) ?> -
    +
    + isPermanent()): ?> + insert('project/widgets/normal_permanent', ['project' => $project]) ?> + + insert('project/widgets/normal', ['project' => $project]) ?> + +
    diff --git a/Resources/templates/responsive/widget/project.php b/Resources/templates/responsive/widget/project.php index 2e4a6206ee..8afb3f1639 100644 --- a/Resources/templates/responsive/widget/project.php +++ b/Resources/templates/responsive/widget/project.php @@ -9,7 +9,11 @@ ?>
    - insert('project/widgets/normal', ['project' => $this->project]) ?> + project->isPermanent()): ?> + insert('project/widgets/normal_permanent', ['project' => $this->project]) ?> + + insert('project/widgets/normal', ['project' => $this->project]) ?> +
    replace() ?> diff --git a/db/migrations/20230324132727_goteo_project_add_type.php b/db/migrations/20230324132727_goteo_project_add_type.php new file mode 100644 index 0000000000..50eb9ba93b --- /dev/null +++ b/db/migrations/20230324132727_goteo_project_add_type.php @@ -0,0 +1,51 @@ +getOption('update'); $project_id = $input->getOption('project'); - $filter = []; + $baseFilter = []; if ($project_id) { $output->writeln("Processing Project [$project_id]:"); - $filter = ['proj_id' => $project_id]; + $baseFilter['proj_id'] = $project_id; } $processed = 0; - $total = Project::getList($filter + ['status' => Project::STATUS_REVIEWING, 'published' => date('Y-m-d')], null, 0, 0, true); + $filter = [ + 'status' => Project::STATUS_REVIEWING, + 'published' => date('Y-m-d'), + ]; + $total = Project::getList($baseFilter + $filter , null, 0, 0, true); $output->writeln("Found $total projects for publishing today"); - foreach(Project::getList($filter + ['status' => Project::STATUS_REVIEWING, 'published' => date('Y-m-d')], null, 0, $total) as $prj) { + foreach(Project::getList($baseFilter + $filter, null, 0, $total) as $prj) { // PUBLISH EVENT $event = new FilterProjectEvent($prj); $output->writeln("Throwing publish project event for {$prj->id}, published {$prj->published}"); @@ -82,9 +87,17 @@ protected function execute(InputInterface $input, OutputInterface $output) } } - $total = Project::getList($filter + ['status' => Project::STATUS_IN_CAMPAIGN], null, 0, 0, true); - $output->writeln("Found $total projects IN CAMPAIGN"); - foreach(Project::getList($filter + ['status' => Project::STATUS_IN_CAMPAIGN], null, 0, $total) as $prj) { + $filter = [ + 'status' => Project::STATUS_IN_CAMPAIGN, + 'type_of_campaign' => Conf::TYPE_CAMPAIGN + ]; + + $total = Project::getList($baseFilter + $filter, null, 0, 0, true); + $output->writeln("Found $total projects IN CAMPAIGN of type CAMPAIGN."); + foreach(Project::getList($baseFilter + $filter, null, 0, $total) as $prj) { + + if ($prj->type != Conf::TYPE_CAMPAIGN) + continue; // a los 5, 3, 2, y 1 dia para finalizar ronda if ($prj->round > 0 && in_array((int) $prj->days, array(5, 3, 2, 1))) { @@ -107,9 +120,13 @@ protected function execute(InputInterface $input, OutputInterface $output) $min_date = date("Y-m-d", mktime(0, 0, 0, date('m'), date('d'), date('Y') - 1)); - $total = Project::getList($filter + ['status' => Project::STATUS_FUNDED, 'succeeded_since' => $min_date], null, 0, 0, true); + $filter = [ + 'status' => Project::STATUS_FUNDED, + 'succeeded_since' => $min_date + ]; + $total = Project::getList($baseFilter + $filter, null, 0, 0, true); $output->writeln("Found $total projects succeeded since $min_date"); - foreach(Project::getList($filter + ['status' => Project::STATUS_FUNDED, 'succeeded_since' => $min_date], null, 0, $total) as $prj) { + foreach(Project::getList($baseFilter + $filter, null, 0, $total) as $prj) { // WATCH EVENT $event = new FilterProjectEvent($prj); diff --git a/src/Goteo/Console/Command/RoundCommand.php b/src/Goteo/Console/Command/RoundCommand.php index 5b9196c904..d23646a405 100644 --- a/src/Goteo/Console/Command/RoundCommand.php +++ b/src/Goteo/Console/Command/RoundCommand.php @@ -16,6 +16,7 @@ use Goteo\Console\Event\FilterProjectEvent; use Goteo\Model\Invest; use Goteo\Model\Project; +use Goteo\Model\Project\Conf; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -73,9 +74,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { $projects = [Project::get($project_id)]; } else { $output->writeln('Processing Active Projects:'); - - // Active projects - $projects = Project::getList(['status' => Project::STATUS_IN_CAMPAIGN], null, 0, 10000); + $projects = Project::getList(['status' => Project::STATUS_IN_CAMPAIGN, 'type_of_campaign' => Conf::TYPE_CAMPAIGN], null, 0, 10000); } if($predict) { @@ -104,6 +103,12 @@ protected function execute(InputInterface $input, OutputInterface $output) { $action_done = false; $return_code = 0; foreach ($projects as $project) { + + if ($project->type != Project\Conf::TYPE_CAMPAIGN) { + $this->warning("This project's type is not a campaign, it is a continuous fundraising project. There are no actions to be taking regarding rounds"); + continue; + } + if ((int) $project->status !== Project::STATUS_IN_CAMPAIGN) { if($force) { $this->warning("Project is not in campaign but force is active. Project [{$project->id}] WILL BE PROCESSED"); diff --git a/src/Goteo/Controller/ChannelController.php b/src/Goteo/Controller/ChannelController.php index a470c40ce9..7fff20d1c7 100644 --- a/src/Goteo/Controller/ChannelController.php +++ b/src/Goteo/Controller/ChannelController.php @@ -524,7 +524,10 @@ public function ajaxSearchAction(Request $request, $id = null) { 'items' => [] ]; foreach($projects as $p) { - $vars['items'][] = View::render('project/widgets/normal', ['project' => $p]); + if ($p->isPermanent()) + $vars['items'][] = View::render('project/widgets/normal_permanent', ['project' => $p]); + else + $vars['items'][] = View::render('project/widgets/normal', ['project' => $p]); } return $this->jsonResponse($vars); } diff --git a/src/Goteo/Controller/DiscoverController.php b/src/Goteo/Controller/DiscoverController.php index e0789b3058..c450cc17b2 100644 --- a/src/Goteo/Controller/DiscoverController.php +++ b/src/Goteo/Controller/DiscoverController.php @@ -16,6 +16,7 @@ use Goteo\Core\Controller; use Goteo\Model\Category; use Goteo\Model\Project; +use Goteo\Model\Project\Conf; use Goteo\Model\Project\ProjectLocation; use Symfony\Component\HttpFoundation\Request; @@ -99,6 +100,11 @@ protected function getProjectFilters($filter, $vars = []): array $filters['status'] = [ Project::STATUS_EDITING, Project::STATUS_REVIEWING, Project::STATUS_IN_CAMPAIGN, Project::STATUS_FUNDED, Project::STATUS_FULFILLED, Project::STATUS_UNFUNDED ]; $filters['is_draft'] = true; } + + if ($filter == 'permanent') { + $filters['type'] = Conf::TYPE_PERMANENT; + } + return $filters; } @@ -142,11 +148,11 @@ public function searchAction (Request $request, $filter = '') { } public function ajaxSearchAction(Request $request) { - $limit = $request->get('limit', 24); - $pag = $request->get('pag', 0); + $limit = $request->query->getDigits('limit', 24); + $pag = $request->query->getDigits('pag', 0); $limit = max(1, min(25, abs($limit))); $pag = max(0, abs($pag)); - $filter = $request->get('filter'); + $filter = $request->query->getAlpha('filter'); $vars = []; if ($request->query->has('q')) @@ -159,7 +165,7 @@ public function ajaxSearchAction(Request $request) { } if ($request->query->has('category')) - $vars['category'] = $request->query->get('category'); + $vars['category'] = $request->query->getAlpha('category'); $ofilters = [ 'status' => [Project::STATUS_IN_CAMPAIGN, Project::STATUS_FUNDED], @@ -186,7 +192,11 @@ public function ajaxSearchAction(Request $request) { 'items' => [] ]; foreach($projects as $p) { - $vars['items'][] = View::render('project/widgets/normal', ['project' => $p]); + if ($p->isPermanent()) { + $vars['items'][] = View::render('project/widgets/normal_permanent', ['project' => $p]); + } else { + $vars['items'][] = View::render('project/widgets/normal', ['project' => $p]); + } } return $this->jsonResponse($vars); diff --git a/src/Goteo/Controller/ProjectController.php b/src/Goteo/Controller/ProjectController.php index 0269b768f2..2d097c23eb 100644 --- a/src/Goteo/Controller/ProjectController.php +++ b/src/Goteo/Controller/ProjectController.php @@ -158,6 +158,7 @@ protected function view(Request $request, $project, $show, $post = null) { $URL = '//'.$request->getHttpHost(); $url = $URL . '/widget/project/' . $project->id; $widget_code = Text::widget($url . $lsuf); + $type = $project->type; // mensaje cuando, sin estar en campaña, tiene fecha de publicación if (!$project->isApproved()) { @@ -244,83 +245,79 @@ protected function view(Request $request, $project, $show, $post = null) { } } - // Custom view data - if ($show == 'home') { - $viewData['types'] = Project\Cost::types(); - // Costs by type - $costs = array(); - - foreach ($project->costs as $cost) { - $costs[$cost->type][] = (object) array( - 'name' => $cost->cost, - 'description' => $cost->description, - 'min' => $cost->required == 1 ? $cost->amount : '', - 'opt' => $cost->amount, - 'req' => $cost->required - ); - } - - $viewData['costs'] = $costs; - $licenses = array(); - - foreach (License::getAll() as $l) { - $licenses[$l->id] = $l; - } - - $viewData['licenses'] = $licenses; - } - - // tenemos que tocar esto un poquito para motrar las necesitades no economicas - if ($show == 'needs-non') { - $viewData['show'] = 'needs'; - $viewData['non_economic'] = true; - } - - // posts - if ($show == 'updates') { - //if is an individual post page - if ($post) { - $pob = BlogPost::getBySlug($post, Lang::current(), $project->lang); - if($pob->slug && $post != $pob->slug) { - return $this->redirect("/project/{$project->id}/updates/{$pob->slug}"); + switch ($show) { + case 'home': + $viewData['types'] = Project\Cost::types(); + // Costs by type + $costs = array(); + + foreach ($project->costs as $cost) { + $costs[$cost->type][] = (object)array( + 'name' => $cost->cost, + 'description' => $cost->description, + 'min' => $cost->required == 1 ? $cost->amount : '', + 'opt' => $cost->amount, + 'req' => $cost->required + ); } - $viewData['post'] = $pob; - $show = 'updates_post'; - } - // sus entradas de novedades - $blog = Blog::get($project->id); - $milestones = ProjectMilestone::getAll($project->id, Lang::current(), $project->lang); - $viewData['milestones']=$milestones; - $viewData['blog'] = $blog; - $viewData['owner'] = $project->owner; + $viewData['costs'] = $costs; + $licenses = array(); - if (empty($user)) { - Message::info(Text::html('user-login-required')); - } - } - - if ($show == 'participate') { - $viewData['worthcracy']=Worth::getAll(); - $limit=15; - $pag = max(0, (int)$request->query->get('pag')); - $viewData['investors_list']= Invest::investors($project->id, false, false, $pag * $limit, $limit, false); - $viewData['investors_total'] = Invest::investors($project->id, false, false, 0, 0, true); - $viewData['investors_limit'] = $limit; + foreach (License::getAll() as $l) { + $licenses[$l->id] = $l; + } - // Collaborations - $viewData['messages'] = SupportMessage::getAll($project->id, Lang::current()); + $viewData['licenses'] = $licenses; + break; + case 'needs-on': + $viewData['show'] = 'needs'; + $viewData['non_economic'] = true; + break; + case 'updates': + //if is an individual post page + if ($post) { + $pob = BlogPost::getBySlug($post, Lang::current(), $project->lang); + if($pob->slug && $post != $pob->slug) { + return $this->redirect("/project/{$project->id}/updates/{$pob->slug}"); + } + $viewData['post'] = $pob; + $show = 'updates_post'; + } - if (empty($user)) { - Message::info(Text::html('user-login-required')); - } - } + // sus entradas de novedades + $blog = Blog::get($project->id); + $milestones = ProjectMilestone::getAll($project->id, Lang::current(), $project->lang); + $viewData['milestones']=$milestones; + $viewData['blog'] = $blog; + $viewData['owner'] = $project->owner; - if ($show == 'messages' && $project->status < 3) { - Message::info(Text::get('project-messages-closed')); + if (empty($user)) { + Message::info(Text::html('user-login-required')); + } + break; + case 'participate': + $viewData['worthcracy']=Worth::getAll(); + $limit=15; + $pag = max(0, (int)$request->query->get('pag')); + $viewData['investors_list']= Invest::investors($project->id, false, false, $pag * $limit, $limit, false); + $viewData['investors_total'] = Invest::investors($project->id, false, false, 0, 0, true); + $viewData['investors_limit'] = $limit; + + // Collaborations + $viewData['messages'] = SupportMessage::getAll($project->id, Lang::current()); + + if (empty($user)) { + Message::info(Text::html('user-login-required')); + } + break; + case 'messages': + if ($project->status < 3) + Message::info(Text::get('project-messages-closed')); + break; } - $response = new Response(View::render('project/'.$show, $viewData)); + $response = new Response(View::render("project/$show", $viewData)); // Force no cache if not approved if(!$project->isApproved()) { $response->headers->set('Pragma', 'no-cache'); diff --git a/src/Goteo/Library/Forms/Model/ProjectCampaignForm.php b/src/Goteo/Library/Forms/Model/ProjectCampaignForm.php index 649e846287..151c8543b9 100644 --- a/src/Goteo/Library/Forms/Model/ProjectCampaignForm.php +++ b/src/Goteo/Library/Forms/Model/ProjectCampaignForm.php @@ -12,9 +12,11 @@ namespace Goteo\Library\Forms\Model; use Goteo\Application\Session; +use Goteo\Core\Exception; use Goteo\Library\Forms\FormProcessorInterface; use Goteo\Library\Forms\AbstractFormProcessor; use Goteo\Library\Forms\FormModelException; +use Goteo\Model\Project; use Goteo\Model\Project\Conf; use Goteo\Util\Form\Type\BooleanType; use Goteo\Util\Form\Type\ChoiceType; @@ -42,18 +44,44 @@ public function createForm(): ProjectCampaignForm $account = $this->getOption('account'); $builder = $this->getBuilder(); + $admin = Session::isAdmin(); + if ($admin) { + $builder + ->add('type', ChoiceType::class, [ + 'label' => 'project-campaign-type-label', + 'row_class' => 'extra', + 'data' => $project->type ?? $project->getConfig()->getType(), + 'choices' => $this->projectTypeChoices(), + 'required' => false + ]) + ->add('impact_calculator', BooleanType::class, [ + 'label' => 'project-campaign-impact-calculator', + 'row_class' => 'extra', + 'data' => $project->isImpactCalcActive(), + 'attr' => [ + 'help' => Text::get('project-campaign-activate-impact-calculator') + ], + 'color' => 'cyan', + 'required' => false + ]); + } + + if ($admin || $project->type != Conf::TYPE_PERMANENT ) { + $builder + ->add('one_round', ChoiceType::class, [ + 'disabled' => $this->getReadonly(), + 'label' => 'costs-field-select-rounds', + 'required' => true, + 'expanded' => true, + 'wrap_class' => 'col-xs-6', + 'choices' => $this->getRoundsAsChoices(), + 'attr' => [ + 'help' => '' . Text::get('tooltip-project-rounds') . '' . Text::get('tooltip-project-2rounds') . '' + ] + ]); + } + $builder - ->add('one_round', ChoiceType::class, [ - 'disabled' => $this->getReadonly(), - 'label' => 'costs-field-select-rounds', - 'required' => true, - 'expanded' => true, - 'wrap_class' => 'col-xs-6', - 'choices' => $this->getRoundsAsChoices(), - 'attr' => [ - 'help' => '' . Text::get('tooltip-project-rounds') . '' . Text::get('tooltip-project-2rounds') . '' - ] - ]) ->add('phone', TextType::class, [ 'label' => 'personal-field-phone', 'disabled' => $this->getReadonly(), @@ -80,20 +108,6 @@ public function createForm(): ProjectCampaignForm ]) ; - $admin = Session::isAdmin(); - if ($admin) { - $builder - ->add('impact_calculator', BooleanType::class, [ - 'label' => 'project-campaign-impact-calculator', - 'row_class' => 'extra', - 'data' => $project->isImpactCalcActive(), - 'attr' => [ - 'help' => Text::get('project-campaign-activate-impact-calculator') - ], - 'color' => 'cyan', - 'required' => false - ]); - } return $this; } @@ -106,6 +120,14 @@ private function getRoundsAsChoices(): array ]; } + private function projectTypeChoices(): array + { + return [ + Text::get('project-campaign-type-campaign') => Conf::TYPE_CAMPAIGN, + Text::get('project-campaign-type-permanent')=> Conf::TYPE_PERMANENT, + ]; + } + public function save(FormInterface $form = null, $force_save = false) { if(!$form) $form = $this->getBuilder()->getForm(); @@ -135,16 +157,26 @@ public function save(FormInterface $form = null, $force_save = false) { } $admin = Session::isAdmin(); - if ($admin && isset($data['impact_calculator'])) { - $conf = Conf::get($project->id); - if ($data['impact_calculator']) { - $conf->activateImpactCalculator(); - } else { - $conf->deactivateImpactCalculator(); - } - $errors = []; - if (!$conf->save($errors)) { - throw new FormModelException(Text::get('form-sent-error', implode(', ',$errors))); + if ($admin) { + try { + $conf = Conf::get($project->id); + if(isset($data['impact_calculator'])) { + if ($data['impact_calculator']) { + $conf->activateImpactCalculator(); + } else { + $conf->deactivateImpactCalculator(); + } + } + + if (isset($data['type'])) { + $conf->setType($data['type']); + } + $errors = []; + if (!$conf->save($errors)) { + throw new FormModelException(Text::get('form-sent-error', implode(', ', $errors))); + } + } catch (Exception $e) { + throw new FormModelException($e->getMessage()); } } @@ -152,5 +184,4 @@ public function save(FormInterface $form = null, $force_save = false) { return $this; } - } diff --git a/src/Goteo/Model/Project.php b/src/Goteo/Model/Project.php index b60c92d97b..8c50fa8f59 100644 --- a/src/Goteo/Model/Project.php +++ b/src/Goteo/Model/Project.php @@ -2317,7 +2317,8 @@ public static function ofmine($owner, $published = false, $offset = 0, $limit = project_conf.noinvest as noinvest, project_conf.one_round as one_round, project_conf.days_round1 as days_round1, - project_conf.days_round2 as days_round2 + project_conf.days_round2 as days_round2, + project_conf.type as type FROM project INNER JOIN user ON user.id = project.owner @@ -3250,6 +3251,14 @@ public static function getList($filters = array(), $node = null, $offset = 0, $l $order = 'ORDER BY project.published DESC'; } } + elseif($filters['type'] == 'permanent') { + $sqlFilter .= ' AND project_conf.type = :type'; + $values[':type'] = ProjectConf::TYPE_PERMANENT; + + if(empty($filters['order'])) { + $order = 'ORDER BY project.published DESC'; + } + } } if (!empty($filters['node'])) { // Check main node in project table and in relation table @@ -3276,6 +3285,11 @@ public static function getList($filters = array(), $node = null, $offset = 0, $l $values[':gender'] = $filters['gender']; } + if (!empty($filters['type_of_campaign'])) { + $sqlFilter .= " AND project_conf.type = :type_of_campaign"; + $values[":type_of_campaign"] = $filters['type_of_campaign']; + } + // order if (in_array($filters['order'], ['updated', 'name'])) { $sqlOrder = " ORDER BY project.{$filters['order']} DESC"; @@ -3808,4 +3822,26 @@ static public function getMatchfundingOwnersGender() { ]; } + public function getConfig(): ProjectConf + { + return ProjectConf::get($this->id); + } + + public function isPermanent(): bool + { + if ($this->type) + return ProjectConf::TYPE_PERMANENT == $this->type; + + $conf = ProjectConf::get($this->id); + return $conf->isTypePermanent(); + } + + public function isCampaign(): bool + { + if ($this->type) + return ProjectConf::TYPE_CAMPAIGN == $this->type; + + $conf = ProjectConf::get($this->id); + return $conf->isTypeCampaign(); + } } diff --git a/src/Goteo/Model/Project/Conf.php b/src/Goteo/Model/Project/Conf.php index 8a1223d840..a7da2fafba 100644 --- a/src/Goteo/Model/Project/Conf.php +++ b/src/Goteo/Model/Project/Conf.php @@ -24,6 +24,9 @@ class Conf extends Model { + const TYPE_CAMPAIGN = 'campaign'; + const TYPE_PERMANENT = 'permanent'; + public $project, $noinvest, // no se pueden hacer más aportes @@ -35,13 +38,16 @@ class Conf extends Model $help_license, $mincost_estimation, $publishing_estimation, - $hide_exhausted_rewards; + $hide_exhausted_rewards + ; private bool $impact_calculator = false; + private string $type = self::TYPE_CAMPAIGN; + /** * Get the conf for a project - * @param varcahr(50) $id Project identifier + * @param string $id Project identifier * @return Conf * @throws Exception */ @@ -90,9 +96,9 @@ public function save(&$errors = array()) if (!$this->validate($errors)) return false; try { - $sql = "REPLACE INTO project_conf (project, noinvest, watch, days_round1, days_round2, one_round, help_cost, help_license, mincost_estimation, publishing_estimation, hide_exhausted_rewards, impact_calculator) VALUES(:project, :noinvest, :watch, :round1, :round2, :one, :helpcost, :helplicense, :mincost_estimation, :publishing_estimation, :hide_exhausted_rewards, :impact_calculator)"; + $sql = "REPLACE INTO project_conf (project, noinvest, watch, days_round1, days_round2, one_round, help_cost, help_license, mincost_estimation, publishing_estimation, hide_exhausted_rewards, impact_calculator, `type`) VALUES(:project, :noinvest, :watch, :round1, :round2, :one, :helpcost, :helplicense, :mincost_estimation, :publishing_estimation, :hide_exhausted_rewards, :impact_calculator, :type)"; $values = [':project' => $this->project, ':noinvest' => $this->noinvest, ':watch' => $this->watch, - ':round1' => $this->days_round1, ':round2' => $this->days_round2, ':one' => $this->one_round, ':helpcost' => $this->help_cost, ':helplicense' => $this->help_license, ':mincost_estimation' => $this->mincost_estimation, ':publishing_estimation' => $this->publishing_estimation, ':hide_exhausted_rewards' => $this->hide_exhausted_rewards, ':impact_calculator' => $this->impact_calculator]; + ':round1' => $this->days_round1, ':round2' => $this->days_round2, ':one' => $this->one_round, ':helpcost' => $this->help_cost, ':helplicense' => $this->help_license, ':mincost_estimation' => $this->mincost_estimation, ':publishing_estimation' => $this->publishing_estimation, ':hide_exhausted_rewards' => $this->hide_exhausted_rewards, ':impact_calculator' => $this->impact_calculator, ':type' => $this->type]; return self::query($sql, $values)?true:false; } catch (\PDOException $e) { $errors[] = "La configuración del proyecto no se ha guardado correctamente. Por favor, revise los datos." . $e->getMessage(); @@ -370,4 +376,24 @@ public function deactivateImpactCalculator() $this->impact_calculator = false; } + public function getType(): string + { + return $this->type; + } + + public function setType(string $type): Conf + { + $this->type = $type; + return $this; + } + + public function isTypePermanent(): bool + { + return self::TYPE_PERMANENT === $this->type; + } + + public function isTypeCampaign(): bool + { + return self::TYPE_CAMPAIGN === $this->type; + } } diff --git a/translations/ca/discover.yml b/translations/ca/discover.yml index 91c49289ce..51eb4d9118 100644 --- a/translations/ca/discover.yml +++ b/translations/ca/discover.yml @@ -7,6 +7,7 @@ discover-group-outdate-header: 'A punt de caducar' discover-group-popular-header: 'Més populars' discover-group-recent-header: 'Recents' discover-group-success-header: 'Reeixits ' +discover-group-permanent-header: 'Permanents' discover-patron-header: '%s recomana
    aquests projectes' discover-results-empty: 'No hem trobat cap projecte que compleixi els criteris de cerca' discover-results-header: 'Resultat de la cerca' diff --git a/translations/ca/project.yml b/translations/ca/project.yml index 7b1fe8f7ee..31052396db 100644 --- a/translations/ca/project.yml +++ b/translations/ca/project.yml @@ -162,3 +162,6 @@ project-impact-calculator-card-impact-description: "Amb un pressupost de %s aspi project-create-continue-impact: "Desar i calcular impacte" project-campaign-impact-calculator: "Calculadora d'Impacte" project-campaign-activate-impact-calculator: "Al activar la calculadora d'impacte, l'impulsora del projecte podrà accedir al módul de les Dades d'Impacte del Projecte, així com també es veurà el seu impacte a la pàgina principal del projecte." +project-campaign-type-label: "Escull tipus de campanya" +project-campaign-type-campaign: "Campanya" +project-campaign-type-permanent: "Permanent" diff --git a/translations/en/discover.yml b/translations/en/discover.yml index 0d741143e8..be0a7573c3 100644 --- a/translations/en/discover.yml +++ b/translations/en/discover.yml @@ -7,6 +7,7 @@ discover-group-outdate-header: 'About to expire' discover-group-popular-header: 'Most popular' discover-group-recent-header: 'Recent' discover-group-success-header: 'Successful' +discover-group-permanent-header: 'Permanents' discover-patron-header: '%s recommend
    these projects' discover-results-empty: 'We did not find any project that matches your search criteria' discover-results-header: 'Search results' diff --git a/translations/en/project.yml b/translations/en/project.yml index 91ef48a272..6721d72704 100644 --- a/translations/en/project.yml +++ b/translations/en/project.yml @@ -159,4 +159,6 @@ project-impact-calculator-card-impact-description: "With a budget of %s we want project-create-continue-impact: "Save and calculate impact" project-campaign-impact-calculator: "Impact Calculator" project-campaign-activate-impact-calculator: "By activating the impact calculator, the project promoter will be able to access the project impact data module, as well as see their impact on the main project page." - +project-campaign-type-label: "Choose type of Campaign" +project-campaign-type-campaign: "Campaign" +project-campaign-type-permanent: "Permanent" diff --git a/translations/es/discover.yml b/translations/es/discover.yml index 06c09ca799..f797f5af83 100644 --- a/translations/es/discover.yml +++ b/translations/es/discover.yml @@ -6,6 +6,7 @@ discover-group-others-header: 'En otros nodos' discover-group-outdate-header: 'A punto de ser archivado' discover-group-popular-header: 'Más populares' discover-group-recent-header: 'Publicados recientemente' +discover-group-permanent-header: 'Permanentes' discover-group-success-header: 'Campañas exitosas' discover-patron-header: '%s recomienda
    estos Proyectos' discover-results-empty: 'No hemos encontrado ningún proyecto que cumpla los criterios de búsqueda' diff --git a/translations/es/project.yml b/translations/es/project.yml index ca1e552ec7..e897a97099 100644 --- a/translations/es/project.yml +++ b/translations/es/project.yml @@ -181,3 +181,6 @@ project-impact-calculator-card-impact-description: "Con un presupuesto de %s asp project-create-continue-impact: "Guardar y calcular impacto" project-campaign-impact-calculator: "Calculadora de Impacto" project-campaign-activate-impact-calculator: "Al activar la calculadora de impacto, la impulsora del proyecto podrá acceder al modulo de los Datos de Impacto del Proyecto, así como también se verá su impacto en la página principal del proyecto." +project-campaign-type-label: "Escoge tipo de campaña" +project-campaign-type-campaign: "Campaña" +project-campaign-type-permanent: "Permanente" From 8ac2172aeb2075993de47549fa81f845c51ee256 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 11 May 2023 16:00:54 +0200 Subject: [PATCH 2/2] modify project lang field to be varchar(3) (#544) --- ...20230511122352_goteo_project_lang_size.php | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 db/migrations/20230511122352_goteo_project_lang_size.php diff --git a/db/migrations/20230511122352_goteo_project_lang_size.php b/db/migrations/20230511122352_goteo_project_lang_size.php new file mode 100644 index 0000000000..47c7abce34 --- /dev/null +++ b/db/migrations/20230511122352_goteo_project_lang_size.php @@ -0,0 +1,58 @@ +