diff --git a/app/BusinessLogicLayer/CrowdSourcingProject/CrowdSourcingProjectManager.php b/app/BusinessLogicLayer/CrowdSourcingProject/CrowdSourcingProjectManager.php index 680085bb..abcd0149 100644 --- a/app/BusinessLogicLayer/CrowdSourcingProject/CrowdSourcingProjectManager.php +++ b/app/BusinessLogicLayer/CrowdSourcingProject/CrowdSourcingProjectManager.php @@ -238,7 +238,7 @@ protected function setDefaultValuesForCommonProjectFields(array $attributes, ?Cr } if (!isset($attributes['about']) || !$attributes['about']) { - $attributes['about'] = $attributes['description']; + $attributes['about'] = trim($attributes['description']); } if ((!isset($attributes['img_path']) || !$attributes['img_path']) && (!$project || !$project->img_path)) { diff --git a/app/BusinessLogicLayer/CrowdSourcingProject/CrowdSourcingProjectTranslationManager.php b/app/BusinessLogicLayer/CrowdSourcingProject/CrowdSourcingProjectTranslationManager.php index d092d750..1b75613a 100644 --- a/app/BusinessLogicLayer/CrowdSourcingProject/CrowdSourcingProjectTranslationManager.php +++ b/app/BusinessLogicLayer/CrowdSourcingProject/CrowdSourcingProjectTranslationManager.php @@ -43,6 +43,11 @@ public function getTranslationsForProject(CrowdSourcingProject $project): Collec public function storeOrUpdateDefaultTranslationForProject(array $attributes, int $project_id): void { $allowedKeys = (new CrowdSourcingProjectTranslation)->getFillable(); $filtered = Helpers::getFilteredAttributes($attributes, $allowedKeys); + // for each of the filtered attributes, if the value is empty, set it to null + // for the WYSIWYG editor, we need to also regard as "empty" the value '
'; + } } diff --git a/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectForLandingPage.php b/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectForLandingPage.php index bb2fd122..bbab9b4a 100644 --- a/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectForLandingPage.php +++ b/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectForLandingPage.php @@ -2,11 +2,10 @@ namespace App\ViewModels\CrowdSourcingProject; +use App\Models\CrowdSourcingProject\CrowdSourcingProject; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Auth; -class CrowdSourcingProjectForLandingPage { - public $project; +class CrowdSourcingProjectForLandingPage extends CrowdSourcingProjectLayoutPage { public $questionnaire; public $feedbackQuestionnaire; public $projectHasPublishedProblems; @@ -22,7 +21,7 @@ class CrowdSourcingProjectForLandingPage { public $moderator; public function __construct( - $project, + CrowdSourcingProject $project, $questionnaire, $feedbackQuestionnaire, $projectHasPublishedProblems, @@ -34,7 +33,7 @@ public function __construct( Collection $languages, $shareUrlForFacebook, $shareUrlForTwitter) { - $this->project = $project; + parent::__construct($project); $this->questionnaire = $questionnaire; $this->feedbackQuestionnaire = $feedbackQuestionnaire; $this->projectHasPublishedProblems = $projectHasPublishedProblems; @@ -50,32 +49,12 @@ public function __construct( $this->moderator = false; } - public function getSignInURLWithParameters(): string { - $url = '/login?submitQuestionnaire=1&redirectTo=' . urlencode($this->project->slug . '?open=1'); - if (Request()->referrerId) { - $url .= urlencode('&referrerId=') . Request()->referrerId; - } - if (Request()->questionnaireId) { - $url .= urlencode('&questionnaireId=') . Request()->questionnaireId; - } - - return $url; - } - public function displayFeedbackQuestionnaire(): bool { // if user has responded to the main questionnaire, // and a feedback questionnaire exists - // and the feedback questionnare has not been answered + // and the feedback questionnaire has not been answered return $this->userResponse != null && $this->feedbackQuestionnaire != null && $this->userFeedbackQuestionnaireResponse == null; } - - public function shouldShowQuestionnaireStatisticsLink(): bool { - return true; - } - - public function getLoggedInUser() { - return Auth::user(); - } } diff --git a/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectLayoutPage.php b/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectLayoutPage.php new file mode 100644 index 00000000..d47dbd98 --- /dev/null +++ b/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectLayoutPage.php @@ -0,0 +1,26 @@ +project = $crowdSourcingProject; + } + + public function projectHasExternalURL(): bool { + return isset($this->project) + && !empty($this->project->external_url) + && $this->project->external_url !== null + && starts_with($this->project->external_url, ['http', 'https']) + && filter_var($this->project->external_url, FILTER_VALIDATE_URL); + } + + public function projectHasCustomFooter(): bool { + return Helpers::HTMLValueIsNotEmpty($this->project->defaultTranslation->footer); + } +} diff --git a/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectUnavailable.php b/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectUnavailable.php index 8d8f3034..443c7304 100644 --- a/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectUnavailable.php +++ b/app/ViewModels/CrowdSourcingProject/CrowdSourcingProjectUnavailable.php @@ -5,18 +5,17 @@ use App\Models\CrowdSourcingProject\CrowdSourcingProject; use Illuminate\Support\Collection; -class CrowdSourcingProjectUnavailable { - public $project; +class CrowdSourcingProjectUnavailable extends CrowdSourcingProjectLayoutPage { public $projects; public $statusMessage; public function __construct(CrowdSourcingProject $project, Collection $projects, string $statusMessage) { - $this->project = $project; + parent::__construct($project); $this->projects = $projects; $this->statusMessage = $statusMessage; } - public function getProjectStatusMessage() { + public function getProjectStatusMessage(): string { return $this->statusMessage; } } diff --git a/app/ViewModels/Problem/ProblemPublicPage.php b/app/ViewModels/Problem/ProblemPublicPage.php index e2cbc9dc..0f3d3b26 100644 --- a/app/ViewModels/Problem/ProblemPublicPage.php +++ b/app/ViewModels/Problem/ProblemPublicPage.php @@ -4,15 +4,15 @@ use App\Models\CrowdSourcingProject\CrowdSourcingProject; use App\Models\Problem\Problem; +use App\ViewModels\CrowdSourcingProject\CrowdSourcingProjectLayoutPage; -class ProblemPublicPage { +class ProblemPublicPage extends CrowdSourcingProjectLayoutPage { public Problem $problem; - public CrowdSourcingProject $project; public string $page_title; public function __construct(Problem $problem, CrowdSourcingProject $project) { + parent::__construct($project); $this->problem = $problem; - $this->project = $project; $this->page_title = $project->currentTranslation->name . ' | ' . $problem->currentTranslation->title; } } diff --git a/app/ViewModels/Problem/ProblemsLandingPage.php b/app/ViewModels/Problem/ProblemsLandingPage.php index 9d51ebf7..6e2fd570 100644 --- a/app/ViewModels/Problem/ProblemsLandingPage.php +++ b/app/ViewModels/Problem/ProblemsLandingPage.php @@ -2,12 +2,6 @@ namespace App\ViewModels\Problem; -use App\Models\CrowdSourcingProject\CrowdSourcingProject; +use App\ViewModels\CrowdSourcingProject\CrowdSourcingProjectLayoutPage; -class ProblemsLandingPage { - public CrowdSourcingProject $project; - - public function __construct(CrowdSourcingProject $crowdSourcingProject) { - $this->project = $crowdSourcingProject; - } -} +class ProblemsLandingPage extends CrowdSourcingProjectLayoutPage {} diff --git a/app/ViewModels/Questionnaire/QuestionnairePage.php b/app/ViewModels/Questionnaire/QuestionnairePage.php index 81d88c6c..581648da 100644 --- a/app/ViewModels/Questionnaire/QuestionnairePage.php +++ b/app/ViewModels/Questionnaire/QuestionnairePage.php @@ -7,11 +7,12 @@ use App\Models\Questionnaire\Questionnaire; use App\Models\Questionnaire\QuestionnaireResponse; use App\Models\User\User; +use App\ViewModels\CrowdSourcingProject\CrowdSourcingProjectLayoutPage; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; -class QuestionnairePage { +class QuestionnairePage extends CrowdSourcingProjectLayoutPage { public Questionnaire $questionnaire; public ?QuestionnaireResponse $userResponse; public CrowdSourcingProject $project; @@ -19,9 +20,9 @@ class QuestionnairePage { public bool $moderator; public function __construct(Questionnaire $questionnaire, ?QuestionnaireResponse $userResponse, CrowdSourcingProject $project, Collection $languages, bool $moderator) { + parent::__construct($project); $this->questionnaire = $questionnaire; $this->userResponse = $userResponse; - $this->project = $project; $this->languages = $languages; $this->moderator = $moderator; } diff --git a/app/ViewModels/Questionnaire/QuestionnaireStatistics.php b/app/ViewModels/Questionnaire/QuestionnaireStatistics.php index bb988e9e..9e935a4d 100644 --- a/app/ViewModels/Questionnaire/QuestionnaireStatistics.php +++ b/app/ViewModels/Questionnaire/QuestionnaireStatistics.php @@ -34,4 +34,12 @@ public function __construct(Questionnaire $questionnaire, public function userCanViewFileTypeQuestionsStatistics(): int { return ($this->userCanAnnotateAnswers || $this->questionnaire->show_file_type_questions_to_statistics_page_audience) ? 1 : -1; } + + public function projectHasExternalURL(): bool { + return false; + } + + public function projectHasCustomFooter(): bool { + return false; + } } diff --git a/app/ViewModels/Solution/ProposeSolutionPage.php b/app/ViewModels/Solution/ProposeSolutionPage.php index 41d104db..537bd9bf 100644 --- a/app/ViewModels/Solution/ProposeSolutionPage.php +++ b/app/ViewModels/Solution/ProposeSolutionPage.php @@ -5,9 +5,9 @@ use App\Models\CrowdSourcingProject\CrowdSourcingProject; use App\Models\Language; use App\Models\Problem\Problem; +use App\ViewModels\CrowdSourcingProject\CrowdSourcingProjectLayoutPage; -class ProposeSolutionPage { - public CrowdSourcingProject $project; +class ProposeSolutionPage extends CrowdSourcingProjectLayoutPage { public Problem $problem; public Language $language; public string $page_title; @@ -17,7 +17,7 @@ public function __construct( Problem $problem, Language $language, ) { - $this->project = $project; + parent::__construct($project); $this->problem = $problem; $this->language = $language; $this->page_title = $project->currentTranslation->name . ' | ' . $problem->currentTranslation->title . ' ➔ ' . __('solution.propose_solution'); diff --git a/app/ViewModels/Solution/SolutionSubmitted.php b/app/ViewModels/Solution/SolutionSubmitted.php index 80fe0edb..de2f6b93 100644 --- a/app/ViewModels/Solution/SolutionSubmitted.php +++ b/app/ViewModels/Solution/SolutionSubmitted.php @@ -5,17 +5,17 @@ use App\Models\CrowdSourcingProject\CrowdSourcingProject; use App\Models\Problem\Problem; use App\Models\Solution\Solution; +use App\ViewModels\CrowdSourcingProject\CrowdSourcingProjectLayoutPage; -class SolutionSubmitted { +class SolutionSubmitted extends CrowdSourcingProjectLayoutPage { public Solution $solution; public Problem $problem; - public CrowdSourcingProject $project; public string $page_title; public function __construct(Solution $solution, Problem $problem, CrowdSourcingProject $project) { + parent::__construct($project); $this->solution = $solution; $this->problem = $problem; - $this->project = $project; $this->page_title = $project->currentTranslation->name . ' | ' . $problem->currentTranslation->title . ' ➔ ' . __('solution.proposal_submitted_title'); } } diff --git a/database/migrations/2024_12_12_101006_make_about_nullable_in_crowd_sourcing_project_translations_table.php b/database/migrations/2024_12_12_101006_make_about_nullable_in_crowd_sourcing_project_translations_table.php new file mode 100644 index 00000000..34718b58 --- /dev/null +++ b/database/migrations/2024_12_12_101006_make_about_nullable_in_crowd_sourcing_project_translations_table.php @@ -0,0 +1,25 @@ +mediumText('about')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::table('crowd_sourcing_project_translations', function (Blueprint $table) { + // + }); + } +}; diff --git a/resources/assets/js/common-backoffice.js b/resources/assets/js/common-backoffice.js index 4db91921..1131bbac 100644 --- a/resources/assets/js/common-backoffice.js +++ b/resources/assets/js/common-backoffice.js @@ -38,7 +38,7 @@ const MOBILE_WIDTH = 768; .slideUp(500, function () { window.$(".alert-dismissable").alert("close"); }); - }, 3000); + }, 3000000); }; const initClipboardElements = function () { diff --git a/resources/assets/js/common.js b/resources/assets/js/common.js index 4ef18173..7399bc7f 100644 --- a/resources/assets/js/common.js +++ b/resources/assets/js/common.js @@ -32,6 +32,14 @@ if (import.meta.env.VITE_SENTRY_DSN_PUBLIC) { }); }; + const trimTextareaInputFields = function () { + // get all textarea elements + // and trim their values + $("textarea").each(function () { + $(this).val($.trim($(this).val())); + }); + }; + const init = function () { $(".dropdown-toggle").dropdown(); handleLogoutBtnClick(); @@ -39,6 +47,7 @@ if (import.meta.env.VITE_SENTRY_DSN_PUBLIC) { $("html, body").animate({ scrollTop: $(this.hash).offset().top - 100 }, 1000); return false; }); + trimTextareaInputFields(); }; $(document).ready(function () { diff --git a/resources/assets/js/vue-components/common/TranslationsManager.vue b/resources/assets/js/vue-components/common/TranslationsManager.vue index 24171f8e..0c2d6de7 100644 --- a/resources/assets/js/vue-components/common/TranslationsManager.vue +++ b/resources/assets/js/vue-components/common/TranslationsManager.vue @@ -239,7 +239,9 @@ export default { const filteredTranslations = (translation) => { // return an object with only the properties that exist in the model metadata return Object.keys(translation).reduce((acc, key) => { - if (propertyExistsInMetadata(translation[key], key) && translation[key] !== "
") { acc[key] = translation[key]; } return acc; diff --git a/resources/assets/sass/shared/alert.scss b/resources/assets/sass/shared/alert.scss index 0c352679..aefb1778 100644 --- a/resources/assets/sass/shared/alert.scss +++ b/resources/assets/sass/shared/alert.scss @@ -5,7 +5,12 @@ z-index: 100000; right: 25px; top: 25px; - width: min(calc(100% - 50px), 26.25rem); + max-width: 90%; + + h4 { + margin: 0; + padding: 10px 0; + } * { color: $white; diff --git a/resources/lang/en/menu.php b/resources/lang/en/menu.php index 01e7e1f1..48b784c0 100644 --- a/resources/lang/en/menu.php +++ b/resources/lang/en/menu.php @@ -32,5 +32,6 @@ 'moderate_solutions' => 'Moderate solutions', 'platform_management' => 'Platform Management', 'manage_users' => 'Manage users', + 'home' => 'Home', ]; diff --git a/resources/views/backoffice/partials/header-controls.blade.php b/resources/views/backoffice/partials/header-controls.blade.php index 16686345..9a754bdc 100644 --- a/resources/views/backoffice/partials/header-controls.blade.php +++ b/resources/views/backoffice/partials/header-controls.blade.php @@ -20,11 +20,7 @@ class="fa fa-chevron-left"> -@if(isset($viewModel->project) && $viewModel->project->defaultTranslation->footer && $viewModel->project->defaultTranslation->footer != "