diff --git a/Resources/templates/responsive/creator/index.php b/Resources/templates/responsive/creator/index.php
new file mode 100644
index 0000000000..b9e4633da9
--- /dev/null
+++ b/Resources/templates/responsive/creator/index.php
@@ -0,0 +1,42 @@
+layout("layout", [
+ 'bodyClass' => 'project creator',
+]);
+
+$permanentProject = $this->permanentProject;
+$listOfProjects = $this->listOfProjects;
+?>
+
+section('head'); ?>
+ = $this->insert('creator/partials/styles') ?>
+append(); ?>
+
+section('content'); ?>
+
+
+
+
+
+
+
+ = $this->insert('project/partials/media', ['project' => $permanentProject ]) ?>
+
+
+
+ = $this->insert('creator/partials/share_social') ?>
+
+ = $this->insert('creator/partials/channel', ['project' => $permanentProject]) ?>
+
+ = $this->insert('creator/partials/subscriptions', ['project' => $permanentProject, 'subscriptions' => $permanentProject->getRewardsOrderBySubscribable()]) ?>
+
+ = $this->insert('creator/partials/posts', ['project' => $permanentProject, 'subscriptions' => $permanentProject->getSubscribableRewards()]) ?>
+
+
+
+append(); ?>
+
+section('footer'); ?>
+ = $this->insert('creator/partials/javascript') ?>
+append(); ?>
diff --git a/Resources/templates/responsive/creator/partials/channel.php b/Resources/templates/responsive/creator/partials/channel.php
new file mode 100644
index 0000000000..60056516bf
--- /dev/null
+++ b/Resources/templates/responsive/creator/partials/channel.php
@@ -0,0 +1,18 @@
+channels;
+ if (!$channels)
+ return;
+?>
+
+
+ = $this->t('regular-channels') ?>
+
+ channels as $channel): ?>
+
+
+
+
+
+
+
+
diff --git a/Resources/templates/responsive/creator/partials/post_item.php b/Resources/templates/responsive/creator/partials/post_item.php
new file mode 100644
index 0000000000..b9982ca96d
--- /dev/null
+++ b/Resources/templates/responsive/creator/partials/post_item.php
@@ -0,0 +1,25 @@
+post;
+ $user = $this->user;
+?>
+
+
+ image): ?>
+
+
+
+
+
+
+
+
+
= $post->subtitle ?>
+
+
+
+
= $user->name ?>
+
+
= date_formater($this->post->date) ?>
+
+
+
diff --git a/Resources/templates/responsive/creator/partials/posts.php b/Resources/templates/responsive/creator/partials/posts.php
new file mode 100644
index 0000000000..675efa4a9c
--- /dev/null
+++ b/Resources/templates/responsive/creator/partials/posts.php
@@ -0,0 +1,15 @@
+posts;
+if (empty($posts))
+ return;
+?>
+
+
+ = $this->t('regular-posts') ?>
+
+
+
+ = $this->insert('creator/partials/post_item', ['post' => $post]); ?>
+
+
+
diff --git a/Resources/templates/responsive/creator/partials/projects.php b/Resources/templates/responsive/creator/partials/projects.php
new file mode 100644
index 0000000000..fefb797b22
--- /dev/null
+++ b/Resources/templates/responsive/creator/partials/projects.php
@@ -0,0 +1,15 @@
+projects;
+if (empty($projects))
+ return;
+?>
+
+
+ = $this->t('regular-projects') ?>
+
+
+ listOfProjects as $project) : ?>
+ = $this->insert('project/widgets/normal', ['project' => $project]) ?>
+
+
+
diff --git a/Resources/templates/responsive/creator/partials/share_social.php b/Resources/templates/responsive/creator/partials/share_social.php
new file mode 100644
index 0000000000..7b0e0c82a9
--- /dev/null
+++ b/Resources/templates/responsive/creator/partials/share_social.php
@@ -0,0 +1,25 @@
+get_url() . '/creator/' . $this->user->id;
+
+$user_twitter = str_replace(
+ [
+ 'https://',
+ 'http://',
+ 'www.',
+ 'twitter.com/',
+ '#!/',
+ '@'
+ ], '', $this->user->twitter);
+$author_share = !empty($user_twitter) ? ' '.$this->text('regular-by').' @'.$user_twitter.' ' : '';
+$share_title = $author_share;
+
+$facebook_url = 'http://facebook.com/sharer.php?u=' . urlencode($share_url) . '&t=' . urlencode($share_title);
+$twitter_url = 'http://twitter.com/intent/tweet?text=' . urlencode($share_title . ': ' . $share_url . ' #Goteo');
+?>
+
diff --git a/Resources/templates/responsive/creator/partials/subscription_item.php b/Resources/templates/responsive/creator/partials/subscription_item.php
new file mode 100644
index 0000000000..d9386a7165
--- /dev/null
+++ b/Resources/templates/responsive/creator/partials/subscription_item.php
@@ -0,0 +1,26 @@
+project;
+$subscription = $this->subscription;
+?>
+
+
+
+
+
+ = amount_format($subscription->amount) ?>
+
+
+
+ = $this->markdown($subscription->description) ?>
+
+
+
+
diff --git a/Resources/templates/responsive/creator/partials/subscriptions.php b/Resources/templates/responsive/creator/partials/subscriptions.php
new file mode 100644
index 0000000000..fa54a629df
--- /dev/null
+++ b/Resources/templates/responsive/creator/partials/subscriptions.php
@@ -0,0 +1,15 @@
+subscriptions;
+if (empty($subscriptions))
+ return;
+?>
+
+
+ = $this->t('regular-subscriptions') ?>
+
+
+
+ = $this->insert('creator/partials/subscription_item', ['subscription' => $subscription]); ?>
+
+
+
diff --git a/public/assets/js/creator/creator.js b/public/assets/js/creator/creator.js
new file mode 100644
index 0000000000..1e33055ed5
--- /dev/null
+++ b/public/assets/js/creator/creator.js
@@ -0,0 +1,79 @@
+/*
+@licstart The following is the entire license notice for the
+JavaScript code in this page.
+
+Copyright (C) 2010 Goteo Foundation
+
+The JavaScript code in this page is free software: you can
+redistribute it and/or modify it under the terms of the GNU
+General Public License (GNU GPL) as published by the Free Software
+Foundation, either version 3 of the License, or (at your option)
+any later version. The code is distributed WITHOUT ANY WARRANTY;
+without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
+
+As additional permission under GNU GPL version 3 section 7, you
+may distribute non-source (e.g., minimized or compacted) forms of
+that code without the copy of the GNU GPL normally required by
+section 4, provided you include this license notice and a URL
+through which recipients can access the Corresponding Source.
+
+
+@licend The above is the entire license notice
+for the JavaScript code in this page.
+*/
+
+$(function(){
+ $('.slider-subscriptions').slick({
+ dots: true,
+ infinite: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ arrows: true,
+ cssEase: 'linear',
+ prevArrow: '
Prev
',
+ nextArrow: 'Next
',
+ responsive: [
+ {
+ breakpoint: 992,
+ settings: {
+ slidesToShow: 2.5,
+ arrows:false
+ }
+ },
+ {
+ breakpoint: 768,
+ settings: {
+ slidesToShow: 1.5,
+ }
+ }
+ ]
+ });
+
+ $('.slider-channel').slick({
+ dots: true,
+ infinite: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ arrows: true,
+ cssEase: 'linear',
+ prevArrow: 'Prev
',
+ nextArrow: 'Next
',
+ responsive: [
+ {
+ breakpoint: 992,
+ settings: {
+ slidesToShow: 2.5,
+ arrows:false
+ }
+ },
+ {
+ breakpoint: 768,
+ settings: {
+ slidesToShow: 1.5,
+ }
+ }
+ ]
+ });
+
+});
diff --git a/public/assets/sass/creator.scss b/public/assets/sass/creator.scss
new file mode 100644
index 0000000000..9a7ee0bb5d
--- /dev/null
+++ b/public/assets/sass/creator.scss
@@ -0,0 +1,259 @@
+@import 'variables';
+
+body.creator {
+
+ section {
+ h2 {
+ font-weight: bold;
+ text-align: center;
+ text-transform: uppercase;
+ }
+ }
+
+ section.subscriptions {
+
+ .slider-subscriptions {
+ .slick-slide {
+ margin: 1em;
+ }
+
+ .custom-left-arrow{
+ top: 34%;
+ position: absolute;
+ font-size: $font-size*5;
+ left: -2.4%;
+ color: $background-light-green;
+ cursor: pointer;
+ }
+ .custom-right-arrow{
+ top: 34%;
+ position: absolute;
+ font-size: $font-size*5;
+ right: -4%;
+ color: $background-light-green;
+ cursor: pointer;
+ }
+ }
+ }
+
+ article.subscription {
+ display: grid;
+ grid-template-rows: auto auto auto;
+ background-color: white;
+ border: 1px solid $color-light-grey;
+ border-radius: 1rem;
+ width: 300px;
+ padding: 2rem;
+ font-size: 1.5rem;
+ transition: all 0.5s ease-in-out;
+
+ &:only-child {
+ grid-column-start: 1;
+ }
+
+ .card-header {
+ h2 {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+
+ .card-body {
+ height: 20rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ padding-bottom: 1rem;
+
+ .amount-box {
+ color: $color-dark-green;
+ font-size: 40px;
+ font-weight: bold;
+ }
+
+ img {
+ width: 100%;
+ }
+
+ &:hover {
+ height: fit-content;
+ min-height: 20rem;
+ }
+ }
+
+ &:hover {
+ border: $background-light-green 2px solid;
+ }
+ }
+
+ section.channel {
+ article.channel {
+ margin-top: unset;
+
+ }
+ }
+
+ section.posts {
+ .post-grid {
+ display: grid;
+ grid-template-columns: 1fr 4fr 1fr;
+
+ gap: 1rem;
+ padding: 1rem;
+ margin-top: 2.5rem;
+
+ article.post {
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-column-start: 2;
+ margin: 20px;
+ border: 1px solid #ddd;
+ border-radius: 8px;
+
+ h2 {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ &:has(.post-image) {
+ grid-template-columns: 1fr 2fr;
+ }
+
+ .post-image {
+ width: 100%;
+
+ img {
+ object-fit: cover;
+ height: 100%;
+ display: block;
+ }
+ }
+
+ .card-body {
+ display: grid;
+ grid-template-rows: 2fr 1fr;
+
+ .card-info {
+ margin: 1em;
+ text-align: center;
+
+ h2 {
+ text-wrap: balance;
+
+ a {
+ color: inherit;
+ }
+ }
+
+ p {
+ display: -webkit-box;
+ text-overflow: ellipsis;
+ line-clamp: 2;
+ }
+ }
+ .card-author-info {
+ padding: 1em;
+ display: flex;
+ align-items: center;
+ background-color: $background-light-green;
+ justify-content: space-between;
+ color: $color-white;
+
+ .author-image {
+ margin-right: 10px;
+
+ img {
+ width: 30px;
+ height: 30px;
+ border-radius: 50%;
+ object-fit: cover;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ section.projects {
+ .project-grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ justify-items: center;
+ grid-column-gap: 3em;
+ grid-row-gap: 3em;
+
+ &:has(> :last-child:nth-child(2)) {
+ grid-template-columns: repeat(2, 1fr);
+
+ .project-widget:first-child {
+ justify-self:right;
+ }
+ .project-widget:last-child {
+ justify-self:left;
+ }
+ }
+
+ padding: 1rem;
+ margin-top: 2.5rem;
+ }
+ }
+
+ section.share-social {
+ display:flex;
+ justify-content: center;
+ margin-top: 2em;
+ color: $primary-color;
+ }
+
+ @media (max-width: $breakpoint-lg) {
+ section.projects .project-grid, section.subscriptions .subscription-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+ }
+
+ section.channel {
+ article.channel {
+ display: grid;
+ justify-content: center;
+
+ a {
+ img {
+ border-radius: 100%;
+ object-fit: contain;
+ }
+ }
+ }
+ }
+
+ @media (max-width: $breakpoint-md) {
+ section.projects .project-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ section.subscriptions .subscription-grid {
+ &:has(> :last-child:nth-child(2)) {
+ grid-template-columns: repeat(2, 1fr);
+ grid-column-gap: 3em;
+
+ article.subscription:first-child {
+ justify-self:right;
+ }
+ article.subscription:last-child {
+ justify-self:left;
+ }
+ }
+ }
+ }
+
+ @media (max-width: $breakpoint-sm) {
+ section.projects div.project-grid, section.subscriptions .subscription-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+ }
+
+ @media (max-width: $breakpoint-xs) {
+ section.projects div.project-grid, section.subscriptions .subscription-grid {
+ grid-template-columns: repeat(1, minmax(0, 1fr));
+ }
+ }
+}
diff --git a/public/templates/responsive/creator/partials/javascript.php b/public/templates/responsive/creator/partials/javascript.php
new file mode 100644
index 0000000000..1eb1c3d429
--- /dev/null
+++ b/public/templates/responsive/creator/partials/javascript.php
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/public/templates/responsive/creator/partials/styles.php b/public/templates/responsive/creator/partials/styles.php
new file mode 100644
index 0000000000..6a2bb74190
--- /dev/null
+++ b/public/templates/responsive/creator/partials/styles.php
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/Goteo/Controller/AdminController.php b/src/Goteo/Controller/AdminController.php
index 30754baf14..2b8b1b85ac 100644
--- a/src/Goteo/Controller/AdminController.php
+++ b/src/Goteo/Controller/AdminController.php
@@ -16,6 +16,7 @@
use Goteo\Application\Message;
use Goteo\Application\Session;
use Goteo\Application\View;
+use Goteo\Controller\Admin\AdminControllerInterface;
use Goteo\Core\Controller;
use Goteo\Library\Feed;
use Goteo\Library\Text;
@@ -135,17 +136,18 @@ public function routingAction(Request $request, $id, $uri = '') {
throw new NotFoundHttpException("Admin module [$id] not found");
}
- public static function createAdminSidebar (User $user, $uri = '') {
-
+ public static function createAdminSidebar(User $user, $uri = '')
+ {
$prefix = '/admin';
foreach (static::$subcontrollers as $id => $class) {
- if(in_array('Goteo\Controller\Admin\AdminControllerInterface', class_implements($class))) {
+ if(in_array(AdminControllerInterface::class, class_implements($class))) {
if(!$class::isAllowed($user)) continue;
$label = $class::getLabel('html');
- $cls = strpos($label, ' $route) {
@@ -153,21 +155,35 @@ public static function createAdminSidebar (User $user, $uri = '') {
if(!is_array($route)) {
$route = ['text' => $route, 'link' => $link];
}
- $c = $route['class'] ? $route['class'] : (strpos($route['text'], ' $route['text'], 'link' => $prefix . $route['link'], 'id' => $route['id'], 'class' => $c];
+ $paths[] = [
+ 'id' => $route['id'],
+ 'text' => $route['text'],
+ 'link' => $prefix . $route['link'],
+ 'class' => $route['class']
+ ? $route['class']
+ : (strpos($route['text'], ' $label, 'link' => "$prefix/$id", 'id' => "/$id", 'class' => $cls];
- $modules[$group ? $group : 'main'][] = $init_route;
+ $modules[$group ? $group : 'main'][] = [
+ 'id' => "/$id",
+ 'text' => $label,
+ 'link' => "$prefix/$id",
+ 'class' => strpos($label, ' $ms) {
@@ -178,9 +194,17 @@ public static function createAdminSidebar (User $user, $uri = '') {
}
}
}
- $modules[$group][] = ['text' => $class::getLabel(), 'link' => "$prefix/$id", 'id' => "/$id", 'class' => 'nopadding'];
+
+ $modules[$group][] = [
+ 'text' => $class::getLabel(),
+ 'link' => "$prefix/$id",
+ 'id' => "/$id",
+ 'class' => 'nopadding'
+ ];
+
}
}
+
// group the modules that don't define a custom menu
$index = 1;
$zone = '';
diff --git a/src/Goteo/Controller/CreatorController.php b/src/Goteo/Controller/CreatorController.php
new file mode 100644
index 0000000000..5a525f181f
--- /dev/null
+++ b/src/Goteo/Controller/CreatorController.php
@@ -0,0 +1,79 @@
+getMessage());
+ return $this->redirect('/');
+ }
+
+ if (!$user instanceof User) {
+ Message::error(Text::get('fatal-error-user'));
+ return $this->redirect('/');
+ }
+
+ if (!$user->hasRole('creator'))
+ return $this->redirectToRoute('user-profile', ['id' => $user->id]);
+
+ /** @var Project $permanentProject */
+ $permanentProject = current(Project::getList(['type_of_campaign' => Project\Conf::TYPE_PERMANENT, 'owner' => $user->id, 'status' => [Project::STATUS_IN_CAMPAIGN]]));
+ $listOfProjects = Project::getList(['type_of_campaign' => Project\Conf::TYPE_CAMPAIGN, 'owner' => $user->id, 'status' => [Project::STATUS_IN_CAMPAIGN, Project::STATUS_FUNDED, Project::STATUS_FULFILLED]]);
+ $posts = Post::getList(['author' => $user->id, 'show' => 'published']);
+ if (empty($permanentProject))
+ return $this->redirect($this->generateUrl('user-profile', ['id' => $user->id]));
+
+ $channels = $this->getChannelsForProject($permanentProject);
+
+ return $this->renderFoilTemplate('creator/index', [
+ 'user' => $user,
+ 'permanentProject' => $permanentProject,
+ 'listOfProjects' => $listOfProjects,
+ 'posts' => $posts,
+ 'channels' => $channels
+ ]);
+ }
+
+
+ /**
+ * @return Node[]
+ */
+ private function getChannelsForProject(Project $project): array
+ {
+ $channels = [];
+
+ $nodeProjectList = NodeProject::getList([
+ 'project' => $project->id
+ ]);
+
+ foreach($nodeProjectList as $node) {
+ $channels[] = Node::get($node->node_id);
+ }
+
+ return $channels;
+ }
+}
diff --git a/src/Goteo/Model/Project.php b/src/Goteo/Model/Project.php
index b4590bd7d3..a6d1cc817d 100644
--- a/src/Goteo/Model/Project.php
+++ b/src/Goteo/Model/Project.php
@@ -753,6 +753,16 @@ public function getIndividualRewards($lang = null) {
return $this->individual_rewards;
}
+ public function getSubscribableRewards(): array
+ {
+ return Reward::getSubscribableRewardsForProject($this);
+ }
+
+ public function getRewardsOrderBySubscribable(): array
+ {
+ return Reward::getRewardsOrderBySubscribableForProject($this);
+ }
+
public function getSupports($lang = null) {
if(!$this->supports) {
// colaboraciones
diff --git a/src/Goteo/Model/Project/Reward.php b/src/Goteo/Model/Project/Reward.php
index 67df0d5954..dbc2eb7675 100644
--- a/src/Goteo/Model/Project/Reward.php
+++ b/src/Goteo/Model/Project/Reward.php
@@ -176,6 +176,75 @@ public static function getAll($project, $type = 'social', $lang = null, $fulfill
}
}
+ /**
+ * @return Reward[]
+ */
+ public static function getSubscribableRewardsForProject(Project $project): array
+ {
+ $lang = Lang::current();
+ list($fields, $joins) = self::getLangsSQLJoins($lang, $project->lang);
+
+ $sql = "SELECT
+ reward.id as id,
+ reward.project as project,
+ $fields,
+ reward.type as type,
+ reward.icon as icon,
+ reward.license as license,
+ reward.amount as amount,
+ reward.units as units,
+ reward.fulsocial as fulsocial,
+ reward.url,
+ reward.bonus,
+ reward.category,
+ reward.subscribable
+ FROM reward
+ $joins
+ WHERE
+ reward.project = ? AND reward.subscribable = 1
+ ORDER BY ISNULL(reward.amount) ASC, ISNULL(reward.reward) ASC, ISNULL(reward.description) ASC
+ ";
+ try {
+ $query = self::query($sql, [$project->id]);
+ return $query->fetchAll(\PDO::FETCH_CLASS, __CLASS__);
+ } catch (\PDOException $e) {
+ throw new ModelException($e->getMessage());
+ }
+ }
+
+ public static function getRewardsOrderBySubscribableForProject(Project $project): array
+ {
+ $lang = Lang::current();
+ list($fields, $joins) = self::getLangsSQLJoins($lang, $project->lang);
+
+ $sql = "SELECT
+ reward.id as id,
+ reward.project as project,
+ $fields,
+ reward.type as type,
+ reward.icon as icon,
+ reward.license as license,
+ reward.amount as amount,
+ reward.units as units,
+ reward.fulsocial as fulsocial,
+ reward.url,
+ reward.bonus,
+ reward.category,
+ reward.subscribable
+ FROM reward
+ $joins
+ WHERE
+ reward.project = ?
+ ORDER BY reward.subscribable DESC, reward.amount ASC
+ ";
+ try {
+ $query = self::query($sql, [$project->id]);
+ return $query->fetchAll(\PDO::FETCH_CLASS, __CLASS__);
+ } catch (\PDOException $e) {
+ throw new ModelException($e->getMessage());
+ }
+ }
+
public static function getWidget($project, $lang = null) {
if(empty($lang)) $lang = Lang::current();
diff --git a/src/routes.php b/src/routes.php
index d72d7cacd8..97c162b4eb 100644
--- a/src/routes.php
+++ b/src/routes.php
@@ -307,4 +307,9 @@
]
));
+$routes->add('creator', new Route(
+ '/creator/{user_id}',
+ ['_controller' => 'Goteo\Controller\CreatorController::indexAction']
+));
+
return $routes;
diff --git a/translations/ca/general.yml b/translations/ca/general.yml
index eb5d46ae5a..a026193b5e 100644
--- a/translations/ca/general.yml
+++ b/translations/ca/general.yml
@@ -260,4 +260,6 @@ regular-type: "Tipus"
regular-source: "Font"
regular-result-msg: "Missatge de resultat"
regular-operation-type: "Operació de càlcul"
-
+regular-posts: "Publicacions"
+regular-subscriptions: "Subscripcions"
+regular-channels: "Canals"
diff --git a/translations/en/general.yml b/translations/en/general.yml
index c971cb71fd..4aba3380aa 100644
--- a/translations/en/general.yml
+++ b/translations/en/general.yml
@@ -266,3 +266,6 @@ regular-type: "Type"
regular-source: "Source"
regular-result-msg: "Result message"
regular-operation-type: "Operation type"
+regular-posts: "Posts"
+regular-subscriptions: "Subscriptions"
+regular-channels: "Channels"
diff --git a/translations/es/general.yml b/translations/es/general.yml
index b5a79fc30f..d62f6fbb84 100644
--- a/translations/es/general.yml
+++ b/translations/es/general.yml
@@ -282,3 +282,6 @@ regular-type: "Tipo"
regular-source: "Fuente"
regular-result-msg: "Mensaje de resultado"
regular-operation-type: "Operación de cálculo"
+regular-posts: "Publicaciones"
+regular-subscriptions: "Suscripciones"
+regular-channels: "Canales"