diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 595c9d584..1fe27f6c6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,8 +1,10 @@ --- -name: 🐛 Bug Report +name: "\U0001F41B Bug Report" about: Create a report to help us to improve. +title: "\U0001F41B [Bug] XXX" labels: bug -title: 🐛 [Bug] +assignees: '' + --- ## :bug: Bug Report diff --git a/.github/ISSUE_TEMPLATE/devops_ticket.md b/.github/ISSUE_TEMPLATE/devops_ticket.md index 115664911..17533cd54 100644 --- a/.github/ISSUE_TEMPLATE/devops_ticket.md +++ b/.github/ISSUE_TEMPLATE/devops_ticket.md @@ -1,8 +1,10 @@ --- -name: 💥 DevOps Ticket +name: "\U0001F4A5 DevOps Ticket" about: Help us manage our deployed app. +title: "\U0001F4A5 [DevOps] XXX" labels: devops -title: 💥 [DevOps] +assignees: '' + --- ## 💥 DevOps Ticket diff --git a/.github/ISSUE_TEMPLATE/epic.md b/.github/ISSUE_TEMPLATE/epic.md index cf72cd673..57eca6dfe 100644 --- a/.github/ISSUE_TEMPLATE/epic.md +++ b/.github/ISSUE_TEMPLATE/epic.md @@ -1,9 +1,12 @@ --- -name: 🌟 Epic +name: "\U0001F31F Epic" about: Define a big development step. +title: "\U0001F31F [EPIC] XXX" labels: epic -title: 🌟 [EPIC] +assignees: '' + --- + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index beae80901..22cd5045e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,8 +1,10 @@ --- -name: 🚀 Feature Request +name: "\U0001F680 Feature Request" about: Suggest an idea for this project. +title: "\U0001F680 [Feature] XXX" labels: feature -title: 🚀 [Feature] +assignees: '' + --- ## :rocket: Feature Request diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 40e6e381b..f2328dcc7 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -1,9 +1,12 @@ --- -name: 💬 Question +name: "\U0001F4AC Question" about: If you need help understanding ocelot.social. +title: "\U0001F4AC [Question] XXX" labels: question -title: 💬 [Question] +assignees: '' + --- + diff --git a/.github/ISSUE_TEMPLATE/refactor_tickets.md b/.github/ISSUE_TEMPLATE/refactor_tickets.md index d1841e35e..867c809ae 100644 --- a/.github/ISSUE_TEMPLATE/refactor_tickets.md +++ b/.github/ISSUE_TEMPLATE/refactor_tickets.md @@ -1,10 +1,11 @@ --- -name: 🔧 Refactor +name: "\U0001F527 Refactor" about: Help us improve our code by refactoring it. +title: "\U0001F527 [Refactor] XXX" labels: refactor -title: 🔧 [Refactor] +assignees: '' + --- ## 🔧 Refactor - diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f8aa04ee..74f2c1dcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,12 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -#### [1.0.9](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.8...1.0.9) +#### [1.1.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.8...1.1.0) +- feat: Make Categories Optional [`#5102`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5102) +- Update issue templates [`#5101`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5101) +- chore: 🍰 Betters Automatic Deployment To `stage.ocelot.social` On Push To `master` Branch [`#5097`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5097) +- chore: 🍰 Release v1.0.9 [`#5095`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5095) - chore: 🍰 Automatic Deployment To `stage.ocelot.social` On Push To `master` Branch [`#5080`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5080) - change footer version-link [`#5091`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5091) - docs: 🍰 Add Neo4j Docu For Important Commands [`#5090`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5090) diff --git a/backend/.env.template b/backend/.env.template index 5858a5d1e..239046dd3 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -28,3 +28,5 @@ AWS_BUCKET= EMAIL_DEFAULT_SENDER="devops@ocelot.social" EMAIL_SUPPORT="devops@ocelot.social" + +CATEGORIES_ACTIVE=false \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 028b9295e..62188a650 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-backend", - "version": "1.0.9", + "version": "1.1.0", "description": "GraphQL Backend for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/backend/src/config/index.js b/backend/src/config/index.js index 6ad8c578b..7df780cfc 100644 --- a/backend/src/config/index.js +++ b/backend/src/config/index.js @@ -86,6 +86,7 @@ const options = { ORGANIZATION_URL: emails.ORGANIZATION_LINK, PUBLIC_REGISTRATION: env.PUBLIC_REGISTRATION === 'true' || false, INVITE_REGISTRATION: env.INVITE_REGISTRATION !== 'false', // default = true + CATEGORIES_ACTIVE: process.env.CATEGORIES_ACTIVE === 'true' || false, } // Check if all required configs are present diff --git a/backend/src/schema/resolvers/posts.js b/backend/src/schema/resolvers/posts.js index d199b6f09..b09bb3edd 100644 --- a/backend/src/schema/resolvers/posts.js +++ b/backend/src/schema/resolvers/posts.js @@ -5,6 +5,7 @@ import { UserInputError } from 'apollo-server' import { mergeImage, deleteImage } from './images/images' import Resolver from './helpers/Resolver' import { filterForMutedUsers } from './helpers/filterForMutedUsers' +import CONFIG from '../../config' const maintainPinnedPosts = (params) => { const pinnedPostFilter = { pinned: true } @@ -76,12 +77,20 @@ export default { }, Mutation: { CreatePost: async (_parent, params, context, _resolveInfo) => { + const { categoryIds } = params const { image: imageInput } = params delete params.categoryIds delete params.image params.id = params.id || uuid() const session = context.driver.session() const writeTxResultPromise = session.writeTransaction(async (transaction) => { + const categoriesCypher = + CONFIG.CATEGORIES_ACTIVE && categoryIds + ? `WITH post + UNWIND $categoryIds AS categoryId + MATCH (category:Category {id: categoryId}) + MERGE (post)-[:CATEGORIZED]->(category)` + : '' const createPostTransactionResponse = await transaction.run( ` CREATE (post:Post) @@ -93,9 +102,10 @@ export default { WITH post MATCH (author:User {id: $userId}) MERGE (post)<-[:WROTE]-(author) + ${categoriesCypher} RETURN post {.*} `, - { userId: context.user.id, params }, + { userId: context.user.id, params, categoryIds }, ) const [post] = createPostTransactionResponse.records.map((record) => record.get('post')) if (imageInput) { @@ -127,7 +137,7 @@ export default { WITH post ` - if (categoryIds && categoryIds.length) { + if (CONFIG.CATEGORIES_ACTIVE && categoryIds && categoryIds.length) { const cypherDeletePreviousRelations = ` MATCH (post:Post { id: $params.id })-[previousRelations:CATEGORIZED]->(category:Category) DELETE previousRelations diff --git a/package.json b/package.json index e23a5f5c7..7756ac8e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social", - "version": "1.0.9", + "version": "1.1.0", "description": "Free and open source software program code available to run social networks.", "author": "ocelot.social Community", "license": "MIT", diff --git a/webapp/.env.template b/webapp/.env.template index 7373255a9..0a4c3405f 100644 --- a/webapp/.env.template +++ b/webapp/.env.template @@ -4,3 +4,4 @@ PUBLIC_REGISTRATION=false INVITE_REGISTRATION=true WEBSOCKETS_URI=ws://localhost:3000/api/graphql GRAPHQL_URI=http://localhost:4000/ +CATEGORIES_ACTIVE=false \ No newline at end of file diff --git a/webapp/components/ContributionForm/ContributionForm.spec.js b/webapp/components/ContributionForm/ContributionForm.spec.js index cce187a63..ce432fc42 100644 --- a/webapp/components/ContributionForm/ContributionForm.spec.js +++ b/webapp/components/ContributionForm/ContributionForm.spec.js @@ -58,6 +58,9 @@ describe('ContributionForm.vue', () => { back: jest.fn(), push: jest.fn(), }, + $env: { + CATEGORIES_ACTIVE: false, + }, } propsData = {} }) @@ -132,6 +135,7 @@ describe('ContributionForm.vue', () => { variables: { title: postTitle, content: postContent, + categoryIds: [], id: null, image: null, }, @@ -254,6 +258,7 @@ describe('ContributionForm.vue', () => { variables: { title: propsData.contribution.title, content: propsData.contribution.content, + categoryIds: [], id: propsData.contribution.id, image: { sensitive: false, diff --git a/webapp/components/ContributionForm/ContributionForm.vue b/webapp/components/ContributionForm/ContributionForm.vue index a06679149..3d4bb8e7c 100644 --- a/webapp/components/ContributionForm/ContributionForm.vue +++ b/webapp/components/ContributionForm/ContributionForm.vue @@ -51,6 +51,19 @@ {{ contentLength }} + + + {{ formData.categoryIds.length }} / 3 + +
{{ $t('actions.cancel') }} @@ -69,6 +82,7 @@ import gql from 'graphql-tag' import { mapGetters } from 'vuex' import HcEditor from '~/components/Editor/Editor' import PostMutations from '~/graphql/PostMutations.js' +import CategoriesSelect from '~/components/CategoriesSelect/CategoriesSelect' import ImageUploader from '~/components/ImageUploader/ImageUploader' import links from '~/constants/links.js' import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParamsLink.vue' @@ -78,6 +92,7 @@ export default { HcEditor, ImageUploader, PageParamsLink, + CategoriesSelect, }, props: { contribution: { @@ -86,7 +101,7 @@ export default { }, }, data() { - const { title, content, image } = this.contribution + const { title, content, image, categories } = this.contribution const { sensitive: imageBlurred = false, aspectRatio: imageAspectRatio = null, @@ -94,6 +109,7 @@ export default { } = image || {} return { + categoriesActive: this.$env.CATEGORIES_ACTIVE, links, formData: { title: title || '', @@ -102,11 +118,22 @@ export default { imageAspectRatio, imageType, imageBlurred, + categoryIds: categories ? categories.map((category) => category.id) : [], }, formSchema: { title: { required: true, min: 3, max: 100 }, content: { required: true }, imageBlurred: { required: false }, + categoryIds: { + type: 'array', + required: this.categoriesActive, + validator: (_, value = []) => { + if (this.categoriesActive && (value.length === 0 || value.length > 3)) { + return [new Error(this.$t('common.validations.categories'))] + } + return [] + }, + }, }, loading: false, users: [], @@ -125,7 +152,7 @@ export default { methods: { submit() { let image = null - const { title, content } = this.formData + const { title, content, categoryIds } = this.formData if (this.formData.image) { image = { sensitive: this.formData.imageBlurred, @@ -143,6 +170,7 @@ export default { variables: { title, content, + categoryIds, id: this.contribution.id || null, image, }, diff --git a/webapp/components/PostTeaser/PostTeaser.spec.js b/webapp/components/PostTeaser/PostTeaser.spec.js index 7a37d710f..546c4581d 100644 --- a/webapp/components/PostTeaser/PostTeaser.spec.js +++ b/webapp/components/PostTeaser/PostTeaser.spec.js @@ -47,6 +47,9 @@ describe('PostTeaser', () => { data: { DeletePost: { id: 'deleted-post-id' } }, }), }, + $env: { + CATEGORIES_ACTIVE: false, + }, } getters = { 'auth/isModerator': () => false, diff --git a/webapp/components/PostTeaser/PostTeaser.vue b/webapp/components/PostTeaser/PostTeaser.vue index 949c7032e..a973ca31f 100644 --- a/webapp/components/PostTeaser/PostTeaser.vue +++ b/webapp/components/PostTeaser/PostTeaser.vue @@ -26,7 +26,19 @@ class="footer" v-observe-visibility="(isVisible, entry) => visibilityChanged(isVisible, entry, post.id)" > -
+
+ +
+
{}, }, }, + data() { + return { + categoriesActive: this.$env.CATEGORIES_ACTIVE, + } + }, mounted() { const { image } = this.post if (!image) return diff --git a/webapp/components/_new/features/SearchResults/SearchResults.spec.js b/webapp/components/_new/features/SearchResults/SearchResults.spec.js index b1886a754..b76226547 100644 --- a/webapp/components/_new/features/SearchResults/SearchResults.spec.js +++ b/webapp/components/_new/features/SearchResults/SearchResults.spec.js @@ -24,6 +24,9 @@ describe('SearchResults', () => { beforeEach(() => { mocks = { $t: jest.fn(), + $env: { + CATEGORIES_ACTIVE: false, + }, } getters = { 'auth/user': () => { diff --git a/webapp/config/index.js b/webapp/config/index.js index 00df85bac..db030e929 100644 --- a/webapp/config/index.js +++ b/webapp/config/index.js @@ -33,6 +33,7 @@ const options = { // Cookies COOKIE_EXPIRE_TIME: process.env.COOKIE_EXPIRE_TIME || 730, // Two years by default COOKIE_HTTPS_ONLY: process.env.COOKIE_HTTPS_ONLY || process.env.NODE_ENV === 'production', // ensure true in production if not set explicitly + CATEGORIES_ACTIVE: process.env.CATEGORIES_ACTIVE === 'true' || false, } const CONFIG = { diff --git a/webapp/graphql/Fragments.js b/webapp/graphql/Fragments.js index 7b05e2369..b67851873 100644 --- a/webapp/graphql/Fragments.js +++ b/webapp/graphql/Fragments.js @@ -78,6 +78,12 @@ export const tagsCategoriesAndPinnedFragment = gql` tags { id } + categories { + id + slug + name + icon + } pinnedBy { id name diff --git a/webapp/maintenance/source/package.json b/webapp/maintenance/source/package.json index 3ee1a5b5c..79ced0031 100644 --- a/webapp/maintenance/source/package.json +++ b/webapp/maintenance/source/package.json @@ -1,6 +1,6 @@ { "name": "@ocelot-social/maintenance", - "version": "1.0.9", + "version": "1.1.0", "description": "Maintenance page for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/webapp/package.json b/webapp/package.json index 77455f49f..234149521 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-webapp", - "version": "1.0.9", + "version": "1.1.0", "description": "ocelot.social Frontend", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/webapp/pages/post/_id.spec.js b/webapp/pages/post/_id.spec.js index 7e6812002..cb97a1776 100644 --- a/webapp/pages/post/_id.spec.js +++ b/webapp/pages/post/_id.spec.js @@ -18,6 +18,9 @@ describe('post/_id.vue', () => { slug: 'my-post', }, }, + $env: { + CATEGORIES_ACTIVE: false, + }, } }) diff --git a/webapp/pages/post/_id/_slug/index.spec.js b/webapp/pages/post/_id/_slug/index.spec.js index 4289bb53d..4737386ef 100644 --- a/webapp/pages/post/_id/_slug/index.spec.js +++ b/webapp/pages/post/_id/_slug/index.spec.js @@ -72,6 +72,9 @@ describe('PostSlug', () => { query: jest.fn().mockResolvedValue({ data: { PostEmotionsCountByEmotion: {} } }), }, $scrollTo: jest.fn(), + $env: { + CATEGORIES_ACTIVE: false, + }, } stubs = { HcEditor: { render: () => {}, methods: { insertReply: jest.fn(() => null) } }, diff --git a/webapp/pages/post/_id/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue index b1ca870d7..d02a448da 100644 --- a/webapp/pages/post/_id/_slug/index.vue +++ b/webapp/pages/post/_id/_slug/index.vue @@ -44,6 +44,19 @@

{{ post.title }}

+ +
+ + + + +
+
@@ -91,6 +104,7 @@