baiscs of space edit page
This commit is contained in:
parent
007b7294d9
commit
ded092ed23
@ -9,7 +9,7 @@ from django.utils.translation import gettext as _
|
||||
from rest_framework import permissions
|
||||
from rest_framework.permissions import SAFE_METHODS
|
||||
|
||||
from cookbook.models import ShareLink, Recipe, UserPreference
|
||||
from cookbook.models import ShareLink, Recipe, UserPreference, UserSpace
|
||||
|
||||
|
||||
def get_allowed_groups(groups_required):
|
||||
@ -53,7 +53,6 @@ def is_object_owner(user, obj):
|
||||
Tests if a given user is the owner of a given object
|
||||
test performed by checking user against the objects user
|
||||
and create_by field (if exists)
|
||||
superusers bypass all checks, unauthenticated users cannot own anything
|
||||
:param user django auth user object
|
||||
:param obj any object that should be tested
|
||||
:return: true if user is owner of object, false otherwise
|
||||
@ -66,11 +65,25 @@ def is_object_owner(user, obj):
|
||||
return False
|
||||
|
||||
|
||||
def is_space_owner(user, obj):
|
||||
"""
|
||||
Tests if a given user is the owner the space of a given object
|
||||
:param user django auth user object
|
||||
:param obj any object that should be tested
|
||||
:return: true if user is owner of the objects space, false otherwise
|
||||
"""
|
||||
if not user.is_authenticated:
|
||||
return False
|
||||
try:
|
||||
return obj.get_space().get_owner() == user
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def is_object_shared(user, obj):
|
||||
"""
|
||||
Tests if a given user is shared for a given object
|
||||
test performed by checking user against the objects shared table
|
||||
superusers bypass all checks, unauthenticated users cannot own anything
|
||||
:param user django auth user object
|
||||
:param obj any object that should be tested
|
||||
:return: true if user is shared for object, false otherwise
|
||||
@ -184,7 +197,7 @@ class CustomIsOwner(permissions.BasePermission):
|
||||
verifies user has ownership over object
|
||||
(either user or created_by or user is request user)
|
||||
"""
|
||||
message = _('You cannot interact with this object as it is not owned by you!') # noqa: E501
|
||||
message = _('You cannot interact with this object as it is not owned by you!')
|
||||
|
||||
def has_permission(self, request, view):
|
||||
return request.user.is_authenticated
|
||||
@ -193,6 +206,20 @@ class CustomIsOwner(permissions.BasePermission):
|
||||
return is_object_owner(request.user, obj)
|
||||
|
||||
|
||||
class CustomIsSpaceOwner(permissions.BasePermission):
|
||||
"""
|
||||
Custom permission class for django rest framework views
|
||||
verifies if the user is the owner of the space the object belongs to
|
||||
"""
|
||||
message = _('You cannot interact with this object as it is not owned by you!')
|
||||
|
||||
def has_permission(self, request, view):
|
||||
return request.user.is_authenticated
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
return is_space_owner(request.user, obj)
|
||||
|
||||
|
||||
# TODO function duplicate/too similar name
|
||||
class CustomIsShared(permissions.BasePermission):
|
||||
"""
|
||||
@ -293,7 +320,19 @@ def above_space_user_limit(space):
|
||||
:param space: Space to test for limits
|
||||
:return: Tuple (True if above or equal limit else false, message)
|
||||
"""
|
||||
limit = space.max_users != 0 and UserPreference.objects.filter(space=space).count() > space.max_users
|
||||
limit = space.max_users != 0 and UserSpace.objects.filter(space=space).count() > space.max_users
|
||||
if limit:
|
||||
return True, _('You have more users than allowed in your space.')
|
||||
return False, ''
|
||||
|
||||
|
||||
def switch_user_active_space(user, user_space):
|
||||
"""
|
||||
Switch the currently active space of a user by setting all spaces to inactive and activating the one passed
|
||||
:param user: user to change active space for
|
||||
:param user_space: user space object to activate
|
||||
"""
|
||||
if not user_space.active:
|
||||
UserSpace.objects.filter(user=user).update(active=False) # make sure to deactivate all spaces for a user
|
||||
user_space.active = True
|
||||
user_space.save()
|
||||
|
@ -241,6 +241,9 @@ class Space(ExportModelOperationsMixin('space'), models.Model):
|
||||
food_inherit = models.ManyToManyField(FoodInheritField, blank=True)
|
||||
show_facet_count = models.BooleanField(default=False)
|
||||
|
||||
def get_owner(self):
|
||||
return self.created_by
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
@ -2,7 +2,7 @@ from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
from gettext import gettext as _
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.db.models import Avg, Q, QuerySet, Sum
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
@ -20,7 +20,7 @@ from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Cu
|
||||
RecipeBookEntry, RecipeImport, ShareLink, ShoppingList,
|
||||
ShoppingListEntry, ShoppingListRecipe, Step, Storage, Supermarket,
|
||||
SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Unit,
|
||||
UserFile, UserPreference, ViewLog)
|
||||
UserFile, UserPreference, ViewLog, Space, UserSpace)
|
||||
from cookbook.templatetags.custom_tags import markdown
|
||||
from recipes.settings import MEDIA_URL, AWS_ENABLED
|
||||
|
||||
@ -123,6 +123,65 @@ class SpaceFilterSerializer(serializers.ListSerializer):
|
||||
return super().to_representation(data)
|
||||
|
||||
|
||||
class UserNameSerializer(WritableNestedModelSerializer):
|
||||
username = serializers.SerializerMethodField('get_user_label')
|
||||
|
||||
def get_user_label(self, obj):
|
||||
return obj.get_user_name()
|
||||
|
||||
class Meta:
|
||||
list_serializer_class = SpaceFilterSerializer
|
||||
model = User
|
||||
fields = ('id', 'username')
|
||||
|
||||
|
||||
class GroupSerializer(WritableNestedModelSerializer):
|
||||
def create(self, validated_data):
|
||||
raise ValidationError('Cannot create using this endpoint')
|
||||
|
||||
class Meta:
|
||||
model = Group
|
||||
fields = ('id', 'name')
|
||||
|
||||
|
||||
class SpaceSerializer(serializers.ModelSerializer):
|
||||
user_count = serializers.SerializerMethodField('get_user_count')
|
||||
recipe_count = serializers.SerializerMethodField('get_recipe_count')
|
||||
file_size_mb = serializers.SerializerMethodField('get_file_size_mb')
|
||||
|
||||
def get_user_count(self, obj):
|
||||
return UserSpace.objects.filter(space=obj).count()
|
||||
|
||||
def get_recipe_count(self, obj):
|
||||
return Recipe.objects.filter(space=obj).count()
|
||||
|
||||
def get_file_size_mb(self, obj):
|
||||
try:
|
||||
return UserFile.objects.filter(space=obj).aggregate(Sum('file_size_kb'))['file_size_kb__sum'] / 1000
|
||||
except TypeError:
|
||||
return 0
|
||||
|
||||
def create(self, validated_data):
|
||||
raise ValidationError('Cannot create using this endpoint')
|
||||
|
||||
class Meta:
|
||||
model = Space
|
||||
fields = ('id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'demo', 'food_inherit', 'show_facet_count', 'user_count', 'recipe_count', 'file_size_mb',)
|
||||
read_only_fields = ('id', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'demo',)
|
||||
|
||||
|
||||
class UserSpaceSerializer(serializers.ModelSerializer):
|
||||
user = UserNameSerializer(read_only=True)
|
||||
|
||||
def create(self, validated_data):
|
||||
raise ValidationError('Cannot create using this endpoint')
|
||||
|
||||
class Meta:
|
||||
model = UserSpace
|
||||
fields = ('id', 'user', 'space', 'groups', 'created_at', 'updated_at',)
|
||||
read_only_fields = ('id', 'created_at', 'updated_at', 'space')
|
||||
|
||||
|
||||
class SpacedModelSerializer(serializers.ModelSerializer):
|
||||
def create(self, validated_data):
|
||||
validated_data['space'] = self.context['request'].space
|
||||
@ -142,18 +201,6 @@ class MealTypeSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
|
||||
read_only_fields = ('created_by',)
|
||||
|
||||
|
||||
class UserNameSerializer(WritableNestedModelSerializer):
|
||||
username = serializers.SerializerMethodField('get_user_label')
|
||||
|
||||
def get_user_label(self, obj):
|
||||
return obj.get_user_name()
|
||||
|
||||
class Meta:
|
||||
list_serializer_class = SpaceFilterSerializer
|
||||
model = User
|
||||
fields = ('id', 'username')
|
||||
|
||||
|
||||
class FoodInheritFieldSerializer(WritableNestedModelSerializer):
|
||||
name = serializers.CharField(allow_null=True, allow_blank=True, required=False)
|
||||
field = serializers.CharField(allow_null=True, allow_blank=True, required=False)
|
||||
|
@ -346,7 +346,7 @@
|
||||
|
||||
{% message_of_the_day as message_of_the_day %}
|
||||
{% if message_of_the_day %}
|
||||
<div class="bg-warning" style=" width: 100%; text-align: center!important; color: #ffffff; padding: 8px">
|
||||
<div class="bg-success" style=" width: 100%; text-align: center!important; color: #ffffff; padding: 8px">
|
||||
{{ message_of_the_day }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
55
cookbook/templates/space_manage.html
Normal file
55
cookbook/templates/space_manage.html
Normal file
@ -0,0 +1,55 @@
|
||||
{% extends "base.html" %}
|
||||
{% load render_bundle from webpack_loader %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
|
||||
{% block title %}{% trans 'Search' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col col-12">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'view_space' %}">{% trans 'Space Settings' %}</a></li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col col-12">
|
||||
<h3>
|
||||
<span class="text-muted">{% trans 'Space:' %}</span> {{ request.space.name }}
|
||||
<small>{% if HOSTED %} <a href="https://tandoor.dev/manage">{% trans 'Manage Subscription' %}</a>
|
||||
{% endif %}</small>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="app">
|
||||
<space-manage-view></space-manage-view>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block script %}
|
||||
{% if debug %}
|
||||
<script src="{% url 'js_reverse' %}"></script>
|
||||
{% else %}
|
||||
<script src="{% static 'django_js_reverse/reverse.js' %}"></script>
|
||||
{% endif %}
|
||||
|
||||
<script type="application/javascript">
|
||||
window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}'
|
||||
window.ACTIVE_SPACE_ID = {{ request.space.id }}
|
||||
</script>
|
||||
|
||||
{% render_bundle 'space_manage_view' %}
|
||||
{% endblock %}
|
@ -25,6 +25,7 @@ router.register(r'food', api.FoodViewSet)
|
||||
router.register(r'food-inherit-field', api.FoodInheritFieldViewSet)
|
||||
router.register(r'import-log', api.ImportLogViewSet)
|
||||
router.register(r'export-log', api.ExportLogViewSet)
|
||||
router.register(r'group', api.GroupViewSet)
|
||||
router.register(r'ingredient', api.IngredientViewSet)
|
||||
router.register(r'keyword', api.KeywordViewSet)
|
||||
router.register(r'meal-plan', api.MealPlanViewSet)
|
||||
@ -35,6 +36,7 @@ router.register(r'recipe-book-entry', api.RecipeBookEntryViewSet)
|
||||
router.register(r'shopping-list', api.ShoppingListViewSet)
|
||||
router.register(r'shopping-list-entry', api.ShoppingListEntryViewSet)
|
||||
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
|
||||
router.register(r'space', api.SpaceViewSet)
|
||||
router.register(r'step', api.StepViewSet)
|
||||
router.register(r'storage', api.StorageViewSet)
|
||||
router.register(r'supermarket', api.SupermarketViewSet)
|
||||
@ -46,6 +48,7 @@ router.register(r'unit', api.UnitViewSet)
|
||||
router.register(r'user-file', api.UserFileViewSet)
|
||||
router.register(r'user-name', api.UserNameViewSet, basename='username')
|
||||
router.register(r'user-preference', api.UserPreferenceViewSet)
|
||||
router.register(r'user-space', api.UserSpaceViewSet)
|
||||
router.register(r'view-log', api.ViewLogViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
@ -56,6 +59,7 @@ urlpatterns = [
|
||||
name='change_space_member'),
|
||||
path('no-group', views.no_groups, name='view_no_group'),
|
||||
path('space-overview', views.space_overview, name='view_space_overview'),
|
||||
path('space-manage/<int:space_id>', views.space_manage, name='view_space_manage'),
|
||||
path('switch-space/<int:space_id>', views.switch_space, name='view_switch_space'),
|
||||
path('no-perm', views.no_perm, name='view_no_perm'),
|
||||
path('signup/<slug:token>', views.signup, name='view_signup'), # TODO deprecated with 0.16.2 remove at some point
|
||||
|
@ -11,7 +11,7 @@ from PIL import UnidentifiedImageError
|
||||
from annoying.decorators import ajax_request
|
||||
from annoying.functions import get_object_or_None
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.contrib.postgres.search import TrigramSimilarity
|
||||
from django.core.exceptions import FieldError, ValidationError
|
||||
from django.core.files import File
|
||||
@ -29,26 +29,22 @@ from requests.exceptions import MissingSchema
|
||||
from rest_framework import decorators, status, viewsets
|
||||
from rest_framework.authtoken.models import Token
|
||||
from rest_framework.authtoken.views import ObtainAuthToken
|
||||
from rest_framework.decorators import api_view, permission_classes, schema
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.exceptions import APIException, PermissionDenied
|
||||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.parsers import MultiPartParser
|
||||
from rest_framework.renderers import JSONRenderer, TemplateHTMLRenderer
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.schemas import AutoSchema
|
||||
from rest_framework.throttling import AnonRateThrottle
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ViewSetMixin
|
||||
from treebeard.exceptions import InvalidMoveToDescendant, InvalidPosition, PathOverflow
|
||||
from validators import ValidationFailure
|
||||
|
||||
from cookbook.helper.HelperFunctions import str2bool
|
||||
from cookbook.helper.image_processing import handle_image
|
||||
from cookbook.helper.ingredient_parser import IngredientParser
|
||||
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest, CustomIsOwner,
|
||||
CustomIsShare, CustomIsShared, CustomIsUser,
|
||||
group_required)
|
||||
group_required, CustomIsSpaceOwner)
|
||||
from cookbook.helper.recipe_html_import import get_recipe_from_source
|
||||
from cookbook.helper.recipe_search import RecipeFacet, RecipeSearch, old_search
|
||||
from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper
|
||||
@ -57,7 +53,7 @@ from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilte
|
||||
Recipe, RecipeBook, RecipeBookEntry, ShareLink, ShoppingList,
|
||||
ShoppingListEntry, ShoppingListRecipe, Step, Storage, Supermarket,
|
||||
SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Unit,
|
||||
UserFile, UserPreference, ViewLog)
|
||||
UserFile, UserPreference, ViewLog, Space, UserSpace)
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.local import Local
|
||||
from cookbook.provider.nextcloud import Nextcloud
|
||||
@ -78,7 +74,7 @@ from cookbook.serializer import (AutomationSerializer, BookmarkletImportSerializ
|
||||
SupermarketCategorySerializer, SupermarketSerializer,
|
||||
SyncLogSerializer, SyncSerializer, UnitSerializer,
|
||||
UserFileSerializer, UserNameSerializer, UserPreferenceSerializer,
|
||||
ViewLogSerializer, IngredientSimpleSerializer, BookmarkletImportListSerializer, RecipeFromSourceSerializer)
|
||||
ViewLogSerializer, IngredientSimpleSerializer, BookmarkletImportListSerializer, RecipeFromSourceSerializer, SpaceSerializer, UserSpaceSerializer, GroupSerializer)
|
||||
from recipes import settings
|
||||
|
||||
|
||||
@ -369,6 +365,33 @@ class UserNameViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
return queryset
|
||||
|
||||
|
||||
class GroupViewSet(viewsets.ModelViewSet):
|
||||
queryset = Group.objects.all()
|
||||
serializer_class = GroupSerializer
|
||||
permission_classes = [CustomIsAdmin]
|
||||
http_method_names = ['get', ]
|
||||
|
||||
|
||||
class SpaceViewSet(viewsets.ModelViewSet):
|
||||
queryset = Space.objects
|
||||
serializer_class = SpaceSerializer
|
||||
permission_classes = [CustomIsOwner]
|
||||
http_method_names = ['get', 'patch']
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(id=self.request.space.id)
|
||||
|
||||
|
||||
class UserSpaceViewSet(viewsets.ModelViewSet):
|
||||
queryset = UserSpace.objects
|
||||
serializer_class = UserSpaceSerializer
|
||||
permission_classes = [CustomIsSpaceOwner]
|
||||
http_method_names = ['get', 'patch', 'delete']
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(space=self.request.space)
|
||||
|
||||
|
||||
class UserPreferenceViewSet(viewsets.ModelViewSet):
|
||||
queryset = UserPreference.objects
|
||||
serializer_class = UserPreferenceSerializer
|
||||
|
@ -26,7 +26,7 @@ from cookbook.filters import RecipeFilter
|
||||
from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, ShoppingPreferenceForm,
|
||||
SpaceCreateForm, SpaceJoinForm, SpacePreferenceForm, User,
|
||||
UserCreateForm, UserNameForm, UserPreference, UserPreferenceForm)
|
||||
from cookbook.helper.permission_helper import group_required, has_group_permission, share_link_valid
|
||||
from cookbook.helper.permission_helper import group_required, has_group_permission, share_link_valid, switch_user_active_space
|
||||
from cookbook.models import (Comment, CookLog, Food, InviteLink, Keyword,
|
||||
MealPlan, RecipeImport, SearchFields, SearchPreference, ShareLink,
|
||||
Space, Unit, ViewLog, UserSpace)
|
||||
@ -144,9 +144,7 @@ def space_overview(request):
|
||||
@login_required
|
||||
def switch_space(request, space_id):
|
||||
user_space = get_object_or_404(UserSpace, space=space_id, user=request.user)
|
||||
UserSpace.objects.filter(user=request.user).update(active=False) # make sure to deactivate all spaces for a user
|
||||
user_space.active = True
|
||||
user_space.save()
|
||||
switch_user_active_space(request.user, user_space)
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
|
||||
@ -533,6 +531,12 @@ def signup(request, token):
|
||||
return HttpResponseRedirect(reverse('view_invite', args=[token]))
|
||||
|
||||
|
||||
@group_required('admin')
|
||||
def space_manage(request, space_id):
|
||||
user_space = get_object_or_404(UserSpace, space=space_id, user=request.user)
|
||||
switch_user_active_space(request.user, user_space)
|
||||
return render(request, 'space_manage.html', {})
|
||||
|
||||
@group_required('admin')
|
||||
def space(request):
|
||||
space_users = UserSpace.objects.filter(space=request.space).all()
|
||||
|
90
vue/src/apps/SpaceManageView/SpaceManageView.vue
Normal file
90
vue/src/apps/SpaceManageView/SpaceManageView.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col col-12">
|
||||
<div v-if="space !== undefined">
|
||||
Recipes {{ space.recipe_count }} / {{ space.max_recipes }}
|
||||
Users {{ space.user_count }} / {{ space.max_users }}
|
||||
Files {{ space.file_size_mb }} / {{ space.max_file_storage_mb }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col col-12">
|
||||
<div v-if="user_spaces !== undefined">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('User') }}</th>
|
||||
<th>{{ $t('Groups') }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="us in user_spaces" :key="us.id">
|
||||
<td>{{ us.user.username }}</td>
|
||||
<td>
|
||||
<generic-multiselect
|
||||
class="input-group-text m-0 p-0"
|
||||
@change="us.groups = $event.val"
|
||||
label="name"
|
||||
:initial_selection="us.groups"
|
||||
:model="Models.GROUP"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
:limit="10"
|
||||
:multiple="true"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<button @click="alert('')">{{ $t('Delete') }}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from "vue"
|
||||
import {BootstrapVue} from "bootstrap-vue"
|
||||
|
||||
import "bootstrap-vue/dist/bootstrap-vue.css"
|
||||
|
||||
import {ApiMixin, ResolveUrlMixin, ToastMixin} from "@/utils/utils"
|
||||
|
||||
import {ApiApiFactory} from "@/utils/openapi/api.ts"
|
||||
import GenericMultiselect from "@/components/GenericMultiselect";
|
||||
|
||||
Vue.use(BootstrapVue)
|
||||
|
||||
export default {
|
||||
name: "SupermarketView",
|
||||
mixins: [ResolveUrlMixin, ToastMixin, ApiMixin],
|
||||
components: {GenericMultiselect},
|
||||
data() {
|
||||
return {
|
||||
space: undefined,
|
||||
user_spaces: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||
|
||||
let apiFactory = new ApiApiFactory()
|
||||
apiFactory.retrieveSpace(window.ACTIVE_SPACE_ID).then(r => {
|
||||
this.space = r.data
|
||||
})
|
||||
apiFactory.listUserSpaces().then(r => {
|
||||
this.user_spaces = r.data
|
||||
})
|
||||
},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
18
vue/src/apps/SpaceManageView/main.js
Normal file
18
vue/src/apps/SpaceManageView/main.js
Normal file
@ -0,0 +1,18 @@
|
||||
import Vue from 'vue'
|
||||
import App from './SpaceManageView.vue'
|
||||
import i18n from '@/i18n'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
// TODO move this and other default stuff to centralized JS file (verify nothing breaks)
|
||||
let publicPath = localStorage.STATIC_URL + 'vue/'
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
publicPath = 'http://localhost:8080/'
|
||||
}
|
||||
export default __webpack_public_path__ = publicPath // eslint-disable-line
|
||||
|
||||
|
||||
new Vue({
|
||||
i18n,
|
||||
render: h => h(App),
|
||||
}).$mount('#app')
|
@ -23,7 +23,7 @@ export class Models {
|
||||
false: undefined,
|
||||
},
|
||||
},
|
||||
tree: { default: undefined },
|
||||
tree: {default: undefined},
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
@ -50,7 +50,7 @@ export class Models {
|
||||
type: "lookup",
|
||||
field: "target",
|
||||
list: "self",
|
||||
sticky_options: [{ id: 0, name: "tree_root" }],
|
||||
sticky_options: [{id: 0, name: "tree_root"}],
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -71,7 +71,7 @@ export class Models {
|
||||
food_onhand: true,
|
||||
shopping: true,
|
||||
},
|
||||
tags: [{ field: "supermarket_category", label: "name", color: "info" }],
|
||||
tags: [{field: "supermarket_category", label: "name", color: "info"}],
|
||||
// REQUIRED: unordered array of fields that can be set during create
|
||||
create: {
|
||||
// if not defined partialUpdate will use the same parameters, prepending 'id'
|
||||
@ -159,7 +159,7 @@ export class Models {
|
||||
field: "substitute_siblings",
|
||||
label: "substitute_siblings", // form.label always translated in utils.getForm()
|
||||
help_text: "substitute_siblings_help", // form.help_text always translated
|
||||
condition: { field: "parent", value: true, condition: "field_exists" },
|
||||
condition: {field: "parent", value: true, condition: "field_exists"},
|
||||
},
|
||||
substitute_children: {
|
||||
form_field: true,
|
||||
@ -168,7 +168,7 @@ export class Models {
|
||||
field: "substitute_children",
|
||||
label: "substitute_children",
|
||||
help_text: "substitute_children_help",
|
||||
condition: { field: "numchild", value: 0, condition: "gt" },
|
||||
condition: {field: "numchild", value: 0, condition: "gt"},
|
||||
},
|
||||
inherit_fields: {
|
||||
form_field: true,
|
||||
@ -178,7 +178,7 @@ export class Models {
|
||||
field: "inherit_fields",
|
||||
list: "FOOD_INHERIT_FIELDS",
|
||||
label: "InheritFields",
|
||||
condition: { field: "food_children_exist", value: true, condition: "preference_equals" },
|
||||
condition: {field: "food_children_exist", value: true, condition: "preference_equals"},
|
||||
help_text: "InheritFields_help",
|
||||
},
|
||||
child_inherit_fields: {
|
||||
@ -189,7 +189,7 @@ export class Models {
|
||||
field: "child_inherit_fields",
|
||||
list: "FOOD_INHERIT_FIELDS",
|
||||
label: "ChildInheritFields", // form.label always translated in utils.getForm()
|
||||
condition: { field: "numchild", value: 0, condition: "gt" },
|
||||
condition: {field: "numchild", value: 0, condition: "gt"},
|
||||
help_text: "ChildInheritFields_help", // form.help_text always translated
|
||||
},
|
||||
reset_inherit: {
|
||||
@ -199,7 +199,7 @@ export class Models {
|
||||
field: "reset_inherit",
|
||||
label: "reset_children",
|
||||
help_text: "reset_children_help",
|
||||
condition: { field: "numchild", value: 0, condition: "gt" },
|
||||
condition: {field: "numchild", value: 0, condition: "gt"},
|
||||
},
|
||||
form_function: "FoodCreateDefault",
|
||||
},
|
||||
@ -399,7 +399,7 @@ export class Models {
|
||||
static SUPERMARKET = {
|
||||
name: "Supermarket",
|
||||
apiName: "Supermarket",
|
||||
ordered_tags: [{ field: "category_to_supermarket", label: "category::name", color: "info" }],
|
||||
ordered_tags: [{field: "category_to_supermarket", label: "category::name", color: "info"}],
|
||||
create: {
|
||||
params: [["name", "description", "category_to_supermarket"]],
|
||||
form: {
|
||||
@ -469,9 +469,9 @@ export class Models {
|
||||
form_field: true,
|
||||
type: "choice",
|
||||
options: [
|
||||
{ value: "FOOD_ALIAS", text: "Food_Alias" },
|
||||
{ value: "UNIT_ALIAS", text: "Unit_Alias" },
|
||||
{ value: "KEYWORD_ALIAS", text: "Keyword_Alias" },
|
||||
{value: "FOOD_ALIAS", text: "Food_Alias"},
|
||||
{value: "UNIT_ALIAS", text: "Unit_Alias"},
|
||||
{value: "KEYWORD_ALIAS", text: "Keyword_Alias"},
|
||||
],
|
||||
field: "type",
|
||||
label: "Type",
|
||||
@ -669,6 +669,12 @@ export class Models {
|
||||
paginated: false,
|
||||
}
|
||||
|
||||
static GROUP = {
|
||||
name: "Group",
|
||||
apiName: "Group",
|
||||
paginated: false,
|
||||
}
|
||||
|
||||
static STEP = {
|
||||
name: "Step",
|
||||
apiName: "Step",
|
||||
@ -694,7 +700,7 @@ export class Actions {
|
||||
},
|
||||
],
|
||||
},
|
||||
ok_label: { function: "translate", phrase: "Save" },
|
||||
ok_label: {function: "translate", phrase: "Save"},
|
||||
},
|
||||
}
|
||||
static UPDATE = {
|
||||
@ -729,7 +735,7 @@ export class Actions {
|
||||
},
|
||||
],
|
||||
},
|
||||
ok_label: { function: "translate", phrase: "Delete" },
|
||||
ok_label: {function: "translate", phrase: "Delete"},
|
||||
instruction: {
|
||||
form_field: true,
|
||||
type: "instruction",
|
||||
@ -756,17 +762,17 @@ export class Actions {
|
||||
suffix: "s",
|
||||
params: ["query", "page", "pageSize", "options"],
|
||||
config: {
|
||||
query: { default: undefined },
|
||||
page: { default: 1 },
|
||||
pageSize: { default: 25 },
|
||||
query: {default: undefined},
|
||||
page: {default: 1},
|
||||
pageSize: {default: 25},
|
||||
},
|
||||
}
|
||||
static MERGE = {
|
||||
function: "merge",
|
||||
params: ["source", "target"],
|
||||
config: {
|
||||
source: { type: "string" },
|
||||
target: { type: "string" },
|
||||
source: {type: "string"},
|
||||
target: {type: "string"},
|
||||
},
|
||||
form: {
|
||||
title: {
|
||||
@ -781,7 +787,7 @@ export class Actions {
|
||||
},
|
||||
],
|
||||
},
|
||||
ok_label: { function: "translate", phrase: "Merge" },
|
||||
ok_label: {function: "translate", phrase: "Merge"},
|
||||
instruction: {
|
||||
form_field: true,
|
||||
type: "instruction",
|
||||
@ -815,8 +821,8 @@ export class Actions {
|
||||
function: "move",
|
||||
params: ["source", "target"],
|
||||
config: {
|
||||
source: { type: "string" },
|
||||
target: { type: "string" },
|
||||
source: {type: "string"},
|
||||
target: {type: "string"},
|
||||
},
|
||||
form: {
|
||||
title: {
|
||||
@ -831,7 +837,7 @@ export class Actions {
|
||||
},
|
||||
],
|
||||
},
|
||||
ok_label: { function: "translate", phrase: "Move" },
|
||||
ok_label: {function: "translate", phrase: "Move"},
|
||||
instruction: {
|
||||
form_field: true,
|
||||
type: "instruction",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -57,6 +57,10 @@ const pages = {
|
||||
entry: "./src/apps/ShoppingListView/main.js",
|
||||
chunks: ["chunk-vendors"],
|
||||
},
|
||||
space_manage_view: {
|
||||
entry: "./src/apps/SpaceManageView/main.js",
|
||||
chunks: ["chunk-vendors"],
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
Loading…
Reference in New Issue
Block a user