Merge pull request #2950 from smilerz/deprecate_settings_form
deprecate unused forms
This commit is contained in:
commit
778f40eac4
@ -10,20 +10,19 @@ from django_scopes import scopes_disabled
|
||||
from django_scopes.forms import SafeModelChoiceField, SafeModelMultipleChoiceField
|
||||
from hcaptcha.fields import hCaptchaField
|
||||
|
||||
|
||||
|
||||
from .models import (Comment, Food, InviteLink, Keyword, Recipe, RecipeBook, RecipeBookEntry,
|
||||
SearchPreference, Space, Storage, Sync, User, UserPreference)
|
||||
from .models import Comment, Food, InviteLink, Keyword, Recipe, RecipeBook, RecipeBookEntry, SearchPreference, Space, Storage, Sync, User, UserPreference
|
||||
|
||||
|
||||
class SelectWidget(widgets.Select):
|
||||
|
||||
class Media:
|
||||
js = ('custom/js/form_select.js',)
|
||||
js = ('custom/js/form_select.js', )
|
||||
|
||||
|
||||
class MultiSelectWidget(widgets.SelectMultiple):
|
||||
|
||||
class Media:
|
||||
js = ('custom/js/form_multiselect.js',)
|
||||
js = ('custom/js/form_multiselect.js', )
|
||||
|
||||
|
||||
# Yes there are some stupid browsers that still dont support this but
|
||||
@ -43,9 +42,7 @@ class UserNameForm(forms.ModelForm):
|
||||
model = User
|
||||
fields = ('first_name', 'last_name')
|
||||
|
||||
help_texts = {
|
||||
'first_name': _('Both fields are optional. If none are given the username will be displayed instead')
|
||||
}
|
||||
help_texts = {'first_name': _('Both fields are optional. If none are given the username will be displayed instead')}
|
||||
|
||||
|
||||
class ExternalRecipeForm(forms.ModelForm):
|
||||
@ -59,23 +56,14 @@ class ExternalRecipeForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Recipe
|
||||
fields = (
|
||||
'name', 'description', 'servings', 'working_time', 'waiting_time',
|
||||
'file_path', 'file_uid', 'keywords'
|
||||
)
|
||||
fields = ('name', 'description', 'servings', 'working_time', 'waiting_time', 'file_path', 'file_uid', 'keywords')
|
||||
|
||||
labels = {
|
||||
'name': _('Name'),
|
||||
'keywords': _('Keywords'),
|
||||
'working_time': _('Preparation time in minutes'),
|
||||
'waiting_time': _('Waiting time (cooking/baking) in minutes'),
|
||||
'file_path': _('Path'),
|
||||
'file_uid': _('Storage UID'),
|
||||
'name': _('Name'), 'keywords': _('Keywords'), 'working_time': _('Preparation time in minutes'), 'waiting_time': _('Waiting time (cooking/baking) in minutes'),
|
||||
'file_path': _('Path'), 'file_uid': _('Storage UID'),
|
||||
}
|
||||
widgets = {'keywords': MultiSelectWidget}
|
||||
field_classes = {
|
||||
'keywords': SafeModelMultipleChoiceField,
|
||||
}
|
||||
field_classes = {'keywords': SafeModelMultipleChoiceField, }
|
||||
|
||||
|
||||
class ImportExportBase(forms.Form):
|
||||
@ -102,14 +90,11 @@ class ImportExportBase(forms.Form):
|
||||
REZEPTSUITEDE = 'REZEPTSUITEDE'
|
||||
PDF = 'PDF'
|
||||
|
||||
type = forms.ChoiceField(choices=(
|
||||
(DEFAULT, _('Default')), (PAPRIKA, 'Paprika'), (NEXTCLOUD, 'Nextcloud Cookbook'),
|
||||
(MEALIE, 'Mealie'), (CHOWDOWN, 'Chowdown'), (SAFFRON, 'Saffron'), (CHEFTAP, 'ChefTap'),
|
||||
(PEPPERPLATE, 'Pepperplate'), (RECETTETEK, 'RecetteTek'), (RECIPESAGE, 'Recipe Sage'), (DOMESTICA, 'Domestica'),
|
||||
(MEALMASTER, 'MealMaster'), (REZKONV, 'RezKonv'), (OPENEATS, 'Openeats'), (RECIPEKEEPER, 'Recipe Keeper'),
|
||||
(PLANTOEAT, 'Plantoeat'), (COOKBOOKAPP, 'CookBookApp'), (COPYMETHAT, 'CopyMeThat'), (PDF, 'PDF'), (MELARECIPES, 'Melarecipes'),
|
||||
(COOKMATE, 'Cookmate'), (REZEPTSUITEDE, 'Recipesuite.de')
|
||||
))
|
||||
type = forms.ChoiceField(choices=((DEFAULT, _('Default')), (PAPRIKA, 'Paprika'), (NEXTCLOUD, 'Nextcloud Cookbook'), (MEALIE, 'Mealie'), (CHOWDOWN, 'Chowdown'),
|
||||
(SAFFRON, 'Saffron'), (CHEFTAP, 'ChefTap'), (PEPPERPLATE, 'Pepperplate'), (RECETTETEK, 'RecetteTek'), (RECIPESAGE, 'Recipe Sage'),
|
||||
(DOMESTICA, 'Domestica'), (MEALMASTER, 'MealMaster'), (REZKONV, 'RezKonv'), (OPENEATS, 'Openeats'), (RECIPEKEEPER, 'Recipe Keeper'),
|
||||
(PLANTOEAT, 'Plantoeat'), (COOKBOOKAPP, 'CookBookApp'), (COPYMETHAT, 'CopyMeThat'), (PDF, 'PDF'), (MELARECIPES, 'Melarecipes'),
|
||||
(COOKMATE, 'Cookmate'), (REZEPTSUITEDE, 'Recipesuite.de')))
|
||||
|
||||
|
||||
class MultipleFileInput(forms.ClearableFileInput):
|
||||
@ -117,6 +102,7 @@ class MultipleFileInput(forms.ClearableFileInput):
|
||||
|
||||
|
||||
class MultipleFileField(forms.FileField):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault("widget", MultipleFileInput())
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -132,9 +118,8 @@ class MultipleFileField(forms.FileField):
|
||||
|
||||
class ImportForm(ImportExportBase):
|
||||
files = MultipleFileField(required=True)
|
||||
duplicates = forms.BooleanField(help_text=_(
|
||||
'To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.'),
|
||||
required=False)
|
||||
duplicates = forms.BooleanField(help_text=_('To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.'),
|
||||
required=False)
|
||||
|
||||
|
||||
class ExportForm(ImportExportBase):
|
||||
@ -153,60 +138,44 @@ class CommentForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = ('text',)
|
||||
fields = ('text', )
|
||||
|
||||
labels = {
|
||||
'text': _('Add your comment: '),
|
||||
}
|
||||
widgets = {
|
||||
'text': forms.Textarea(attrs={'rows': 2, 'cols': 15}),
|
||||
}
|
||||
labels = {'text': _('Add your comment: '), }
|
||||
widgets = {'text': forms.Textarea(attrs={'rows': 2, 'cols': 15}), }
|
||||
|
||||
|
||||
class StorageForm(forms.ModelForm):
|
||||
username = forms.CharField(
|
||||
widget=forms.TextInput(attrs={'autocomplete': 'new-password'}),
|
||||
required=False
|
||||
)
|
||||
password = forms.CharField(
|
||||
widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'}),
|
||||
required=False,
|
||||
help_text=_('Leave empty for dropbox and enter app password for nextcloud.')
|
||||
)
|
||||
token = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
attrs={'autocomplete': 'new-password', 'type': 'password'}
|
||||
),
|
||||
required=False,
|
||||
help_text=_('Leave empty for nextcloud and enter api token for dropbox.')
|
||||
)
|
||||
username = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password'}), required=False)
|
||||
password = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'}),
|
||||
required=False,
|
||||
help_text=_('Leave empty for dropbox and enter app password for nextcloud.'))
|
||||
token = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'}),
|
||||
required=False,
|
||||
help_text=_('Leave empty for nextcloud and enter api token for dropbox.'))
|
||||
|
||||
class Meta:
|
||||
model = Storage
|
||||
fields = ('name', 'method', 'username', 'password', 'token', 'url', 'path')
|
||||
|
||||
help_texts = {
|
||||
'url': _(
|
||||
'Leave empty for dropbox and enter only base url for nextcloud (<code>/remote.php/webdav/</code> is added automatically)'),
|
||||
}
|
||||
help_texts = {'url': _('Leave empty for dropbox and enter only base url for nextcloud (<code>/remote.php/webdav/</code> is added automatically)'), }
|
||||
|
||||
|
||||
# TODO: Deprecate
|
||||
class RecipeBookEntryForm(forms.ModelForm):
|
||||
prefix = 'bookmark'
|
||||
# class RecipeBookEntryForm(forms.ModelForm):
|
||||
# prefix = 'bookmark'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
space = kwargs.pop('space')
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['book'].queryset = RecipeBook.objects.filter(space=space).all()
|
||||
# def __init__(self, *args, **kwargs):
|
||||
# space = kwargs.pop('space')
|
||||
# super().__init__(*args, **kwargs)
|
||||
# self.fields['book'].queryset = RecipeBook.objects.filter(space=space).all()
|
||||
|
||||
class Meta:
|
||||
model = RecipeBookEntry
|
||||
fields = ('book',)
|
||||
# class Meta:
|
||||
# model = RecipeBookEntry
|
||||
# fields = ('book',)
|
||||
|
||||
field_classes = {
|
||||
'book': SafeModelChoiceField,
|
||||
}
|
||||
# field_classes = {
|
||||
# 'book': SafeModelChoiceField,
|
||||
# }
|
||||
|
||||
|
||||
class SyncForm(forms.ModelForm):
|
||||
@ -220,25 +189,15 @@ class SyncForm(forms.ModelForm):
|
||||
model = Sync
|
||||
fields = ('storage', 'path', 'active')
|
||||
|
||||
field_classes = {
|
||||
'storage': SafeModelChoiceField,
|
||||
}
|
||||
field_classes = {'storage': SafeModelChoiceField, }
|
||||
|
||||
labels = {
|
||||
'storage': _('Storage'),
|
||||
'path': _('Path'),
|
||||
'active': _('Active')
|
||||
}
|
||||
labels = {'storage': _('Storage'), 'path': _('Path'), 'active': _('Active')}
|
||||
|
||||
|
||||
# TODO deprecate
|
||||
class BatchEditForm(forms.Form):
|
||||
search = forms.CharField(label=_('Search String'))
|
||||
keywords = forms.ModelMultipleChoiceField(
|
||||
queryset=Keyword.objects.none(),
|
||||
required=False,
|
||||
widget=MultiSelectWidget
|
||||
)
|
||||
keywords = forms.ModelMultipleChoiceField(queryset=Keyword.objects.none(), required=False, widget=MultiSelectWidget)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
space = kwargs.pop('space')
|
||||
@ -247,6 +206,7 @@ class BatchEditForm(forms.Form):
|
||||
|
||||
|
||||
class ImportRecipeForm(forms.ModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
space = kwargs.pop('space')
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -256,19 +216,13 @@ class ImportRecipeForm(forms.ModelForm):
|
||||
model = Recipe
|
||||
fields = ('name', 'keywords', 'file_path', 'file_uid')
|
||||
|
||||
labels = {
|
||||
'name': _('Name'),
|
||||
'keywords': _('Keywords'),
|
||||
'file_path': _('Path'),
|
||||
'file_uid': _('File ID'),
|
||||
}
|
||||
labels = {'name': _('Name'), 'keywords': _('Keywords'), 'file_path': _('Path'), 'file_uid': _('File ID'), }
|
||||
widgets = {'keywords': MultiSelectWidget}
|
||||
field_classes = {
|
||||
'keywords': SafeModelChoiceField,
|
||||
}
|
||||
field_classes = {'keywords': SafeModelChoiceField, }
|
||||
|
||||
|
||||
class InviteLinkForm(forms.ModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -276,8 +230,8 @@ class InviteLinkForm(forms.ModelForm):
|
||||
|
||||
def clean(self):
|
||||
space = self.cleaned_data['space']
|
||||
if space.max_users != 0 and (UserPreference.objects.filter(space=space).count() +
|
||||
InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, space=space).count()) >= space.max_users:
|
||||
if space.max_users != 0 and (UserPreference.objects.filter(space=space).count()
|
||||
+ InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, space=space).count()) >= space.max_users:
|
||||
raise ValidationError(_('Maximum number of users for this space reached.'))
|
||||
|
||||
def clean_email(self):
|
||||
@ -291,12 +245,8 @@ class InviteLinkForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = InviteLink
|
||||
fields = ('email', 'group', 'valid_until', 'space')
|
||||
help_texts = {
|
||||
'email': _('An email address is not required but if present the invite link will be sent to the user.'),
|
||||
}
|
||||
field_classes = {
|
||||
'space': SafeModelChoiceField,
|
||||
}
|
||||
help_texts = {'email': _('An email address is not required but if present the invite link will be sent to the user.'), }
|
||||
field_classes = {'space': SafeModelChoiceField, }
|
||||
|
||||
|
||||
class SpaceCreateForm(forms.Form):
|
||||
@ -342,133 +292,109 @@ class CustomPasswordResetForm(ResetPasswordForm):
|
||||
|
||||
class UserCreateForm(forms.Form):
|
||||
name = forms.CharField(label='Username')
|
||||
password = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
attrs={'autocomplete': 'new-password', 'type': 'password'}
|
||||
)
|
||||
)
|
||||
password_confirm = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
attrs={'autocomplete': 'new-password', 'type': 'password'}
|
||||
)
|
||||
)
|
||||
password = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'}))
|
||||
password_confirm = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'}))
|
||||
|
||||
|
||||
class SearchPreferenceForm(forms.ModelForm):
|
||||
prefix = 'search'
|
||||
trigram_threshold = forms.DecimalField(min_value=0.01, max_value=1, decimal_places=2,
|
||||
trigram_threshold = forms.DecimalField(min_value=0.01,
|
||||
max_value=1,
|
||||
decimal_places=2,
|
||||
widget=NumberInput(attrs={'class': "form-control-range", 'type': 'range'}),
|
||||
help_text=_(
|
||||
'Determines how fuzzy a search is if it uses trigram similarity matching (e.g. low values mean more typos are ignored).'))
|
||||
help_text=_('Determines how fuzzy a search is if it uses trigram similarity matching (e.g. low values mean more typos are ignored).'))
|
||||
preset = forms.CharField(widget=forms.HiddenInput(), required=False)
|
||||
|
||||
class Meta:
|
||||
model = SearchPreference
|
||||
fields = (
|
||||
'search', 'lookup', 'unaccent', 'icontains', 'istartswith', 'trigram', 'fulltext', 'trigram_threshold')
|
||||
fields = ('search', 'lookup', 'unaccent', 'icontains', 'istartswith', 'trigram', 'fulltext', 'trigram_threshold')
|
||||
|
||||
help_texts = {
|
||||
'search': _(
|
||||
'Select type method of search. Click <a href="/docs/search/">here</a> for full description of choices.'),
|
||||
'lookup': _('Use fuzzy matching on units, keywords and ingredients when editing and importing recipes.'),
|
||||
'unaccent': _(
|
||||
'Fields to search ignoring accents. Selecting this option can improve or degrade search quality depending on language'),
|
||||
'icontains': _(
|
||||
"Fields to search for partial matches. (e.g. searching for 'Pie' will return 'pie' and 'piece' and 'soapie')"),
|
||||
'istartswith': _(
|
||||
"Fields to search for beginning of word matches. (e.g. searching for 'sa' will return 'salad' and 'sandwich')"),
|
||||
'trigram': _(
|
||||
"Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) Note: this option will conflict with 'web' and 'raw' methods of search."),
|
||||
'fulltext': _(
|
||||
"Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods only function with fulltext fields."),
|
||||
'search': _('Select type method of search. Click <a href="/docs/search/">here</a> for full description of choices.'), 'lookup':
|
||||
_('Use fuzzy matching on units, keywords and ingredients when editing and importing recipes.'), 'unaccent':
|
||||
_('Fields to search ignoring accents. Selecting this option can improve or degrade search quality depending on language'), 'icontains':
|
||||
_("Fields to search for partial matches. (e.g. searching for 'Pie' will return 'pie' and 'piece' and 'soapie')"), 'istartswith':
|
||||
_("Fields to search for beginning of word matches. (e.g. searching for 'sa' will return 'salad' and 'sandwich')"), 'trigram':
|
||||
_("Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) Note: this option will conflict with 'web' and 'raw' methods of search."), 'fulltext':
|
||||
_("Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods only function with fulltext fields."),
|
||||
}
|
||||
|
||||
labels = {
|
||||
'search': _('Search Method'),
|
||||
'lookup': _('Fuzzy Lookups'),
|
||||
'unaccent': _('Ignore Accent'),
|
||||
'icontains': _("Partial Match"),
|
||||
'istartswith': _("Starts With"),
|
||||
'trigram': _("Fuzzy Search"),
|
||||
'fulltext': _("Full Text")
|
||||
'search': _('Search Method'), 'lookup': _('Fuzzy Lookups'), 'unaccent': _('Ignore Accent'), 'icontains': _("Partial Match"), 'istartswith': _("Starts With"),
|
||||
'trigram': _("Fuzzy Search"), 'fulltext': _("Full Text")
|
||||
}
|
||||
|
||||
widgets = {
|
||||
'search': SelectWidget,
|
||||
'unaccent': MultiSelectWidget,
|
||||
'icontains': MultiSelectWidget,
|
||||
'istartswith': MultiSelectWidget,
|
||||
'trigram': MultiSelectWidget,
|
||||
'fulltext': MultiSelectWidget,
|
||||
'search': SelectWidget, 'unaccent': MultiSelectWidget, 'icontains': MultiSelectWidget, 'istartswith': MultiSelectWidget, 'trigram': MultiSelectWidget, 'fulltext':
|
||||
MultiSelectWidget,
|
||||
}
|
||||
|
||||
|
||||
class ShoppingPreferenceForm(forms.ModelForm):
|
||||
prefix = 'shopping'
|
||||
# class ShoppingPreferenceForm(forms.ModelForm):
|
||||
# prefix = 'shopping'
|
||||
|
||||
class Meta:
|
||||
model = UserPreference
|
||||
# class Meta:
|
||||
# model = UserPreference
|
||||
|
||||
fields = (
|
||||
'shopping_share', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'mealplan_autoexclude_onhand',
|
||||
'mealplan_autoinclude_related', 'shopping_add_onhand', 'default_delay', 'filter_to_supermarket', 'shopping_recent_days', 'csv_delim', 'csv_prefix'
|
||||
)
|
||||
# fields = (
|
||||
# 'shopping_share', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'mealplan_autoexclude_onhand',
|
||||
# 'mealplan_autoinclude_related', 'shopping_add_onhand', 'default_delay', 'filter_to_supermarket', 'shopping_recent_days', 'csv_delim', 'csv_prefix'
|
||||
# )
|
||||
|
||||
help_texts = {
|
||||
'shopping_share': _('Users will see all items you add to your shopping list. They must add you to see items on their list.'),
|
||||
'shopping_auto_sync': _(
|
||||
'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit '
|
||||
'of mobile data. If lower than instance limit it is reset when saving.'
|
||||
),
|
||||
'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'),
|
||||
'mealplan_autoinclude_related': _('When adding a meal plan to the shopping list (manually or automatically), include all related recipes.'),
|
||||
'mealplan_autoexclude_onhand': _('When adding a meal plan to the shopping list (manually or automatically), exclude ingredients that are on hand.'),
|
||||
'default_delay': _('Default number of hours to delay a shopping list entry.'),
|
||||
'filter_to_supermarket': _('Filter shopping list to only include supermarket categories.'),
|
||||
'shopping_recent_days': _('Days of recent shopping list entries to display.'),
|
||||
'shopping_add_onhand': _("Mark food 'On Hand' when checked off shopping list."),
|
||||
'csv_delim': _('Delimiter to use for CSV exports.'),
|
||||
'csv_prefix': _('Prefix to add when copying list to the clipboard.'),
|
||||
# help_texts = {
|
||||
# 'shopping_share': _('Users will see all items you add to your shopping list. They must add you to see items on their list.'),
|
||||
# 'shopping_auto_sync': _(
|
||||
# 'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit '
|
||||
# 'of mobile data. If lower than instance limit it is reset when saving.'
|
||||
# ),
|
||||
# 'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'),
|
||||
# 'mealplan_autoinclude_related': _('When adding a meal plan to the shopping list (manually or automatically), include all related recipes.'),
|
||||
# 'mealplan_autoexclude_onhand': _('When adding a meal plan to the shopping list (manually or automatically), exclude ingredients that are on hand.'),
|
||||
# 'default_delay': _('Default number of hours to delay a shopping list entry.'),
|
||||
# 'filter_to_supermarket': _('Filter shopping list to only include supermarket categories.'),
|
||||
# 'shopping_recent_days': _('Days of recent shopping list entries to display.'),
|
||||
# 'shopping_add_onhand': _("Mark food 'On Hand' when checked off shopping list."),
|
||||
# 'csv_delim': _('Delimiter to use for CSV exports.'),
|
||||
# 'csv_prefix': _('Prefix to add when copying list to the clipboard.'),
|
||||
|
||||
}
|
||||
labels = {
|
||||
'shopping_share': _('Share Shopping List'),
|
||||
'shopping_auto_sync': _('Autosync'),
|
||||
'mealplan_autoadd_shopping': _('Auto Add Meal Plan'),
|
||||
'mealplan_autoexclude_onhand': _('Exclude On Hand'),
|
||||
'mealplan_autoinclude_related': _('Include Related'),
|
||||
'default_delay': _('Default Delay Hours'),
|
||||
'filter_to_supermarket': _('Filter to Supermarket'),
|
||||
'shopping_recent_days': _('Recent Days'),
|
||||
'csv_delim': _('CSV Delimiter'),
|
||||
"csv_prefix_label": _("List Prefix"),
|
||||
'shopping_add_onhand': _("Auto On Hand"),
|
||||
}
|
||||
# }
|
||||
# labels = {
|
||||
# 'shopping_share': _('Share Shopping List'),
|
||||
# 'shopping_auto_sync': _('Autosync'),
|
||||
# 'mealplan_autoadd_shopping': _('Auto Add Meal Plan'),
|
||||
# 'mealplan_autoexclude_onhand': _('Exclude On Hand'),
|
||||
# 'mealplan_autoinclude_related': _('Include Related'),
|
||||
# 'default_delay': _('Default Delay Hours'),
|
||||
# 'filter_to_supermarket': _('Filter to Supermarket'),
|
||||
# 'shopping_recent_days': _('Recent Days'),
|
||||
# 'csv_delim': _('CSV Delimiter'),
|
||||
# "csv_prefix_label": _("List Prefix"),
|
||||
# 'shopping_add_onhand': _("Auto On Hand"),
|
||||
# }
|
||||
|
||||
widgets = {
|
||||
'shopping_share': MultiSelectWidget
|
||||
}
|
||||
# widgets = {
|
||||
# 'shopping_share': MultiSelectWidget
|
||||
# }
|
||||
|
||||
# class SpacePreferenceForm(forms.ModelForm):
|
||||
# prefix = 'space'
|
||||
# reset_food_inherit = forms.BooleanField(label=_("Reset Food Inheritance"), initial=False, required=False,
|
||||
# help_text=_("Reset all food to inherit the fields configured."))
|
||||
|
||||
class SpacePreferenceForm(forms.ModelForm):
|
||||
prefix = 'space'
|
||||
reset_food_inherit = forms.BooleanField(label=_("Reset Food Inheritance"), initial=False, required=False,
|
||||
help_text=_("Reset all food to inherit the fields configured."))
|
||||
# def __init__(self, *args, **kwargs):
|
||||
# super().__init__(*args, **kwargs) # populates the post
|
||||
# self.fields['food_inherit'].queryset = Food.inheritable_fields
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs) # populates the post
|
||||
self.fields['food_inherit'].queryset = Food.inheritable_fields
|
||||
# class Meta:
|
||||
# model = Space
|
||||
|
||||
class Meta:
|
||||
model = Space
|
||||
# fields = ('food_inherit', 'reset_food_inherit',)
|
||||
|
||||
fields = ('food_inherit', 'reset_food_inherit',)
|
||||
# help_texts = {
|
||||
# 'food_inherit': _('Fields on food that should be inherited by default.'),
|
||||
# 'use_plural': _('Use the plural form for units and food inside this space.'),
|
||||
# }
|
||||
|
||||
help_texts = {
|
||||
'food_inherit': _('Fields on food that should be inherited by default.'),
|
||||
'use_plural': _('Use the plural form for units and food inside this space.'),
|
||||
}
|
||||
|
||||
widgets = {
|
||||
'food_inherit': MultiSelectWidget
|
||||
}
|
||||
# widgets = {
|
||||
# 'food_inherit': MultiSelectWidget
|
||||
# }
|
||||
|
@ -9,8 +9,7 @@ from django.views.generic import UpdateView
|
||||
from django.views.generic.edit import FormMixin
|
||||
|
||||
from cookbook.forms import CommentForm, ExternalRecipeForm, StorageForm, SyncForm
|
||||
from cookbook.helper.permission_helper import (GroupRequiredMixin, OwnerRequiredMixin,
|
||||
above_space_limit, group_required)
|
||||
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, above_space_limit, group_required
|
||||
from cookbook.models import Comment, Recipe, RecipeImport, Storage, Sync
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.local import Local
|
||||
@ -102,26 +101,16 @@ def edit_storage(request, pk):
|
||||
|
||||
instance.save()
|
||||
|
||||
messages.add_message(
|
||||
request, messages.SUCCESS, _('Storage saved!')
|
||||
)
|
||||
messages.add_message(request, messages.SUCCESS, _('Storage saved!'))
|
||||
else:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('There was an error updating this storage backend!')
|
||||
)
|
||||
messages.add_message(request, messages.ERROR, _('There was an error updating this storage backend!'))
|
||||
else:
|
||||
pseudo_instance = instance
|
||||
pseudo_instance.password = '__NO__CHANGE__'
|
||||
pseudo_instance.token = '__NO__CHANGE__'
|
||||
form = StorageForm(instance=pseudo_instance)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'generic/edit_template.html',
|
||||
{'form': form, 'title': _('Storage')}
|
||||
)
|
||||
return render(request, 'generic/edit_template.html', {'form': form, 'title': _('Storage')})
|
||||
|
||||
|
||||
class CommentUpdate(OwnerRequiredMixin, UpdateView):
|
||||
@ -135,9 +124,7 @@ class CommentUpdate(OwnerRequiredMixin, UpdateView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CommentUpdate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Comment")
|
||||
context['view_url'] = reverse(
|
||||
'view_recipe', args=[self.object.recipe.pk]
|
||||
)
|
||||
context['view_url'] = reverse('view_recipe', args=[self.object.recipe.pk])
|
||||
return context
|
||||
|
||||
|
||||
@ -176,11 +163,7 @@ class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
if self.object.storage.method == Storage.LOCAL:
|
||||
Local.rename_file(old_recipe, self.object.name)
|
||||
|
||||
self.object.file_path = "%s/%s%s" % (
|
||||
os.path.dirname(self.object.file_path),
|
||||
self.object.name,
|
||||
os.path.splitext(self.object.file_path)[1]
|
||||
)
|
||||
self.object.file_path = "%s/%s%s" % (os.path.dirname(self.object.file_path), self.object.name, os.path.splitext(self.object.file_path)[1])
|
||||
|
||||
messages.add_message(self.request, messages.SUCCESS, _('Changes saved!'))
|
||||
return super(ExternalRecipeUpdate, self).form_valid(form)
|
||||
@ -197,7 +180,5 @@ class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
context['title'] = _("Recipe")
|
||||
context['view_url'] = reverse('view_recipe', args=[self.object.pk])
|
||||
if self.object.storage:
|
||||
context['delete_external_url'] = reverse(
|
||||
'delete_recipe_source', args=[self.object.pk]
|
||||
)
|
||||
context['delete_external_url'] = reverse('delete_recipe_source', args=[self.object.pk])
|
||||
return context
|
||||
|
@ -1,10 +1,10 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from io import StringIO
|
||||
from uuid import UUID
|
||||
import subprocess
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
@ -23,17 +23,14 @@ from django.utils import timezone
|
||||
from django.utils.translation import gettext as _
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, SpaceCreateForm,
|
||||
SpaceJoinForm, User, UserCreateForm, UserPreference)
|
||||
from cookbook.forms import CommentForm, Recipe, SearchPreferenceForm, SpaceCreateForm, SpaceJoinForm, User, UserCreateForm, UserPreference
|
||||
from cookbook.helper.HelperFunctions import str2bool
|
||||
from cookbook.helper.permission_helper import (group_required, has_group_permission,
|
||||
share_link_valid, switch_user_active_space)
|
||||
from cookbook.models import (Comment, CookLog, InviteLink, SearchFields, SearchPreference,
|
||||
ShareLink, Space, UserSpace, ViewLog)
|
||||
from cookbook.helper.permission_helper import group_required, has_group_permission, share_link_valid, switch_user_active_space
|
||||
from cookbook.models import Comment, CookLog, InviteLink, SearchFields, SearchPreference, ShareLink, Space, UserSpace, ViewLog
|
||||
from cookbook.tables import CookLogTable, ViewLogTable
|
||||
from cookbook.templatetags.theming_tags import get_theming_values
|
||||
from cookbook.version_info import VERSION_INFO
|
||||
from recipes.settings import PLUGINS, BASE_DIR
|
||||
from recipes.settings import BASE_DIR, PLUGINS
|
||||
|
||||
|
||||
def index(request):
|
||||
@ -44,11 +41,7 @@ def index(request):
|
||||
return HttpResponseRedirect(reverse_lazy('view_search'))
|
||||
|
||||
try:
|
||||
page_map = {
|
||||
UserPreference.SEARCH: reverse_lazy('view_search'),
|
||||
UserPreference.PLAN: reverse_lazy('view_plan'),
|
||||
UserPreference.BOOKS: reverse_lazy('view_books'),
|
||||
}
|
||||
page_map = {UserPreference.SEARCH: reverse_lazy('view_search'), UserPreference.PLAN: reverse_lazy('view_plan'), UserPreference.BOOKS: reverse_lazy('view_books'), }
|
||||
|
||||
return HttpResponseRedirect(page_map.get(request.user.userpreference.default_page))
|
||||
except UserPreference.DoesNotExist:
|
||||
@ -57,7 +50,7 @@ def index(request):
|
||||
|
||||
# TODO need to deprecate
|
||||
def search(request):
|
||||
if has_group_permission(request.user, ('guest',)):
|
||||
if has_group_permission(request.user, ('guest', )):
|
||||
return render(request, 'search.html', {})
|
||||
else:
|
||||
if request.user.is_authenticated:
|
||||
@ -84,14 +77,13 @@ def space_overview(request):
|
||||
_('You have the reached the maximum amount of spaces that can be owned by you.') + f' ({request.user.userpreference.max_owned_spaces})')
|
||||
return HttpResponseRedirect(reverse('view_space_overview'))
|
||||
|
||||
created_space = Space.objects.create(
|
||||
name=create_form.cleaned_data['name'],
|
||||
created_by=request.user,
|
||||
max_file_storage_mb=settings.SPACE_DEFAULT_MAX_FILES,
|
||||
max_recipes=settings.SPACE_DEFAULT_MAX_RECIPES,
|
||||
max_users=settings.SPACE_DEFAULT_MAX_USERS,
|
||||
allow_sharing=settings.SPACE_DEFAULT_ALLOW_SHARING,
|
||||
)
|
||||
created_space = Space.objects.create(name=create_form.cleaned_data['name'],
|
||||
created_by=request.user,
|
||||
max_file_storage_mb=settings.SPACE_DEFAULT_MAX_FILES,
|
||||
max_recipes=settings.SPACE_DEFAULT_MAX_RECIPES,
|
||||
max_users=settings.SPACE_DEFAULT_MAX_USERS,
|
||||
allow_sharing=settings.SPACE_DEFAULT_ALLOW_SHARING,
|
||||
)
|
||||
|
||||
user_space = UserSpace.objects.create(space=created_space, user=request.user, active=False)
|
||||
user_space.groups.add(Group.objects.filter(name='admin').get())
|
||||
@ -135,23 +127,18 @@ def recipe_view(request, pk, share=None):
|
||||
recipe = get_object_or_404(Recipe, pk=pk)
|
||||
|
||||
if not request.user.is_authenticated and not share_link_valid(recipe, share):
|
||||
messages.add_message(request, messages.ERROR,
|
||||
_('You do not have the required permissions to view this page!'))
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!'))
|
||||
return HttpResponseRedirect(reverse('account_login') + '?next=' + request.path)
|
||||
|
||||
if not (has_group_permission(request.user,
|
||||
('guest',)) and recipe.space == request.space) and not share_link_valid(recipe,
|
||||
share):
|
||||
messages.add_message(request, messages.ERROR,
|
||||
_('You do not have the required permissions to view this page!'))
|
||||
if not (has_group_permission(request.user, ('guest', )) and recipe.space == request.space) and not share_link_valid(recipe, share):
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!'))
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
comments = Comment.objects.filter(recipe__space=request.space, recipe=recipe)
|
||||
|
||||
if request.method == "POST":
|
||||
if not request.user.is_authenticated:
|
||||
messages.add_message(request, messages.ERROR,
|
||||
_('You do not have the required permissions to perform this action!'))
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to perform this action!'))
|
||||
return HttpResponseRedirect(reverse('view_recipe', kwargs={'pk': recipe.pk, 'share': share}))
|
||||
|
||||
comment_form = CommentForm(request.POST, prefix='comment')
|
||||
@ -167,16 +154,16 @@ def recipe_view(request, pk, share=None):
|
||||
comment_form = CommentForm()
|
||||
|
||||
if request.user.is_authenticated:
|
||||
if not ViewLog.objects.filter(recipe=recipe, created_by=request.user,
|
||||
created_at__gt=(timezone.now() - timezone.timedelta(minutes=5)),
|
||||
space=request.space).exists():
|
||||
if not ViewLog.objects.filter(recipe=recipe, created_by=request.user, created_at__gt=(timezone.now() - timezone.timedelta(minutes=5)), space=request.space).exists():
|
||||
ViewLog.objects.create(recipe=recipe, created_by=request.user, space=request.space)
|
||||
|
||||
if request.method == "GET":
|
||||
servings = request.GET.get("servings")
|
||||
return render(request, 'recipe_view.html',
|
||||
{'recipe': recipe, 'comments': comments, 'comment_form': comment_form, 'share': share, 'servings': servings })
|
||||
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def books(request):
|
||||
return render(request, 'books.html', {})
|
||||
@ -239,12 +226,8 @@ def shopping_settings(request):
|
||||
if search_form.is_valid():
|
||||
if not sp:
|
||||
sp = SearchPreferenceForm(user=request.user)
|
||||
fields_searched = (
|
||||
len(search_form.cleaned_data['icontains'])
|
||||
+ len(search_form.cleaned_data['istartswith'])
|
||||
+ len(search_form.cleaned_data['trigram'])
|
||||
+ len(search_form.cleaned_data['fulltext'])
|
||||
)
|
||||
fields_searched = (len(search_form.cleaned_data['icontains']) + len(search_form.cleaned_data['istartswith']) + len(search_form.cleaned_data['trigram'])
|
||||
+ len(search_form.cleaned_data['fulltext']))
|
||||
if search_form.cleaned_data['preset'] == 'fuzzy':
|
||||
sp.search = SearchPreference.SIMPLE
|
||||
sp.lookup = True
|
||||
@ -269,13 +252,10 @@ def shopping_settings(request):
|
||||
elif fields_searched == 0:
|
||||
search_form.add_error(None, _('You must select at least one field to search!'))
|
||||
search_error = True
|
||||
elif search_form.cleaned_data['search'] in ['websearch', 'raw'] and len(
|
||||
search_form.cleaned_data['fulltext']) == 0:
|
||||
search_form.add_error('search',
|
||||
_('To use this search method you must select at least one full text search field!'))
|
||||
elif search_form.cleaned_data['search'] in ['websearch', 'raw'] and len(search_form.cleaned_data['fulltext']) == 0:
|
||||
search_form.add_error('search', _('To use this search method you must select at least one full text search field!'))
|
||||
search_error = True
|
||||
elif search_form.cleaned_data['search'] in ['websearch', 'raw'] and len(
|
||||
search_form.cleaned_data['trigram']) > 0:
|
||||
elif search_form.cleaned_data['search'] in ['websearch', 'raw'] and len(search_form.cleaned_data['trigram']) > 0:
|
||||
search_form.add_error(None, _('Fuzzy search is not compatible with this search method!'))
|
||||
search_error = True
|
||||
else:
|
||||
@ -291,8 +271,7 @@ def shopping_settings(request):
|
||||
else:
|
||||
search_error = True
|
||||
|
||||
fields_searched = len(sp.icontains.all()) + len(sp.istartswith.all()) + len(sp.trigram.all()) + len(
|
||||
sp.fulltext.all())
|
||||
fields_searched = len(sp.icontains.all()) + len(sp.istartswith.all()) + len(sp.trigram.all()) + len(sp.fulltext.all())
|
||||
if sp and not search_error and fields_searched > 0:
|
||||
search_form = SearchPreferenceForm(instance=sp)
|
||||
elif not search_error:
|
||||
@ -305,23 +284,13 @@ def shopping_settings(request):
|
||||
sp.fulltext.clear()
|
||||
sp.save()
|
||||
|
||||
return render(request, 'settings.html', {
|
||||
'search_form': search_form,
|
||||
})
|
||||
return render(request, 'settings.html', {'search_form': search_form, })
|
||||
|
||||
|
||||
@group_required('guest')
|
||||
def history(request):
|
||||
view_log = ViewLogTable(
|
||||
ViewLog.objects.filter(
|
||||
created_by=request.user, space=request.space
|
||||
).order_by('-created_at').all()
|
||||
)
|
||||
cook_log = CookLogTable(
|
||||
CookLog.objects.filter(
|
||||
created_by=request.user
|
||||
).order_by('-created_at').all()
|
||||
)
|
||||
view_log = ViewLogTable(ViewLog.objects.filter(created_by=request.user, space=request.space).order_by('-created_at').all())
|
||||
cook_log = CookLogTable(CookLog.objects.filter(created_by=request.user).order_by('-created_at').all())
|
||||
return render(request, 'history.html', {'view_log': view_log, 'cook_log': cook_log})
|
||||
|
||||
|
||||
@ -344,12 +313,10 @@ def system(request):
|
||||
database_message = _('Everything is fine!')
|
||||
elif postgres_ver < postgres_current - 2:
|
||||
database_status = 'danger'
|
||||
database_message = _('PostgreSQL %(v)s is deprecated. Upgrade to a fully supported version!') % {
|
||||
'v': postgres_ver}
|
||||
database_message = _('PostgreSQL %(v)s is deprecated. Upgrade to a fully supported version!') % {'v': postgres_ver}
|
||||
else:
|
||||
database_status = 'info'
|
||||
database_message = _('You are running PostgreSQL %(v1)s. PostgreSQL %(v2)s is recommended') % {
|
||||
'v1': postgres_ver, 'v2': postgres_current}
|
||||
database_message = _('You are running PostgreSQL %(v1)s. PostgreSQL %(v2)s is recommended') % {'v1': postgres_ver, 'v2': postgres_current}
|
||||
else:
|
||||
database_status = 'info'
|
||||
database_message = _(
|
||||
@ -378,34 +345,26 @@ def system(request):
|
||||
pass
|
||||
else:
|
||||
current_app = row
|
||||
migration_info[current_app] = {'app': current_app, 'unapplied_migrations': [], 'applied_migrations': [],
|
||||
'total': 0}
|
||||
migration_info[current_app] = {'app': current_app, 'unapplied_migrations': [], 'applied_migrations': [], 'total': 0}
|
||||
|
||||
for key in migration_info.keys():
|
||||
migration_info[key]['total'] = len(migration_info[key]['unapplied_migrations']) + len(
|
||||
migration_info[key]['applied_migrations'])
|
||||
migration_info[key]['total'] = len(migration_info[key]['unapplied_migrations']) + len(migration_info[key]['applied_migrations'])
|
||||
|
||||
return render(request, 'system.html', {
|
||||
'gunicorn_media': settings.GUNICORN_MEDIA,
|
||||
'debug': settings.DEBUG,
|
||||
'postgres': postgres,
|
||||
'postgres_version': postgres_ver,
|
||||
'postgres_status': database_status,
|
||||
'postgres_message': database_message,
|
||||
'version_info': VERSION_INFO,
|
||||
'plugins': PLUGINS,
|
||||
'secret_key': secret_key,
|
||||
'orphans': orphans,
|
||||
'migration_info': migration_info,
|
||||
'missing_migration': missing_migration,
|
||||
})
|
||||
return render(
|
||||
request, 'system.html', {
|
||||
'gunicorn_media': settings.GUNICORN_MEDIA, 'debug': settings.DEBUG, 'postgres': postgres, 'postgres_version': postgres_ver, 'postgres_status': database_status,
|
||||
'postgres_message': database_message, 'version_info': VERSION_INFO, 'plugins': PLUGINS, 'secret_key': secret_key, 'orphans': orphans, 'migration_info': migration_info,
|
||||
'missing_migration': missing_migration,
|
||||
})
|
||||
|
||||
|
||||
def setup(request):
|
||||
with scopes_disabled():
|
||||
if User.objects.count() > 0 or 'django.contrib.auth.backends.RemoteUserBackend' in settings.AUTHENTICATION_BACKENDS:
|
||||
messages.add_message(request, messages.ERROR,
|
||||
_('The setup page can only be used to create the first user! If you have forgotten your superuser credentials please consult the django documentation on how to reset passwords.'))
|
||||
messages.add_message(
|
||||
request, messages.ERROR,
|
||||
_('The setup page can only be used to create the first user! If you have forgotten your superuser credentials please consult the django documentation on how to reset passwords.'
|
||||
))
|
||||
return HttpResponseRedirect(reverse('account_login'))
|
||||
|
||||
if request.method == 'POST':
|
||||
@ -445,8 +404,7 @@ def invite_link(request, token):
|
||||
link.used_by = request.user
|
||||
link.save()
|
||||
|
||||
user_space = UserSpace.objects.create(user=request.user, space=link.space,
|
||||
internal_note=link.internal_note, invite_link=link, active=False)
|
||||
user_space = UserSpace.objects.create(user=request.user, space=link.space, internal_note=link.internal_note, invite_link=link, active=False)
|
||||
|
||||
if request.user.userspace_set.count() == 1:
|
||||
user_space.active = True
|
||||
@ -476,66 +434,36 @@ def space_manage(request, space_id):
|
||||
|
||||
def report_share_abuse(request, token):
|
||||
if not settings.SHARING_ABUSE:
|
||||
messages.add_message(request, messages.WARNING,
|
||||
_('Reporting share links is not enabled for this instance. Please notify the page administrator to report problems.'))
|
||||
messages.add_message(request, messages.WARNING, _('Reporting share links is not enabled for this instance. Please notify the page administrator to report problems.'))
|
||||
else:
|
||||
if link := ShareLink.objects.filter(uuid=token).first():
|
||||
link.abuse_blocked = True
|
||||
link.save()
|
||||
messages.add_message(request, messages.WARNING,
|
||||
_('Recipe sharing link has been disabled! For additional information please contact the page administrator.'))
|
||||
messages.add_message(request, messages.WARNING, _('Recipe sharing link has been disabled! For additional information please contact the page administrator.'))
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
|
||||
def web_manifest(request):
|
||||
theme_values = get_theming_values(request)
|
||||
|
||||
icons = [
|
||||
{"src": theme_values['logo_color_svg'], "sizes": "any"},
|
||||
{"src": theme_values['logo_color_144'], "type": "image/png", "sizes": "144x144"},
|
||||
{"src": theme_values['logo_color_512'], "type": "image/png", "sizes": "512x512"}
|
||||
]
|
||||
icons = [{"src": theme_values['logo_color_svg'], "sizes": "any"}, {"src": theme_values['logo_color_144'], "type": "image/png", "sizes": "144x144"},
|
||||
{"src": theme_values['logo_color_512'], "type": "image/png", "sizes": "512x512"}]
|
||||
|
||||
manifest_info = {
|
||||
"name": theme_values['app_name'],
|
||||
"short_name": theme_values['app_name'],
|
||||
"description": _("Manage recipes, shopping list, meal plans and more."),
|
||||
"icons": icons,
|
||||
"start_url": "./search",
|
||||
"background_color": theme_values['nav_bg_color'],
|
||||
"display": "standalone",
|
||||
"scope": ".",
|
||||
"theme_color": theme_values['nav_bg_color'],
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": _("Plan"),
|
||||
"short_name": _("Plan"),
|
||||
"description": _("View your meal Plan"),
|
||||
"url": "./plan"
|
||||
},
|
||||
{
|
||||
"name": _("Books"),
|
||||
"short_name": _("Books"),
|
||||
"description": _("View your cookbooks"),
|
||||
"url": "./books"
|
||||
},
|
||||
{
|
||||
"name": _("Shopping"),
|
||||
"short_name": _("Shopping"),
|
||||
"description": _("View your shopping lists"),
|
||||
"url": "./list/shopping-list/"
|
||||
}
|
||||
],
|
||||
"share_target": {
|
||||
"action": "/data/import/url",
|
||||
"method": "GET",
|
||||
"params": {
|
||||
"title": "title",
|
||||
"url": "url",
|
||||
"text": "text"
|
||||
|
||||
}
|
||||
}
|
||||
"name":
|
||||
theme_values['app_name'], "short_name":
|
||||
theme_values['app_name'], "description":
|
||||
_("Manage recipes, shopping list, meal plans and more."), "icons":
|
||||
icons, "start_url":
|
||||
"./search", "background_color":
|
||||
theme_values['nav_bg_color'], "display":
|
||||
"standalone", "scope":
|
||||
".", "theme_color":
|
||||
theme_values['nav_bg_color'], "shortcuts":
|
||||
[{"name": _("Plan"), "short_name": _("Plan"), "description": _("View your meal Plan"), "url":
|
||||
"./plan"}, {"name": _("Books"), "short_name": _("Books"), "description": _("View your cookbooks"), "url": "./books"},
|
||||
{"name": _("Shopping"), "short_name": _("Shopping"), "description": _("View your shopping lists"), "url":
|
||||
"./list/shopping-list/"}], "share_target": {"action": "/data/import/url", "method": "GET", "params": {"title": "title", "url": "url", "text": "text"}}
|
||||
}
|
||||
|
||||
return JsonResponse(manifest_info, json_dumps_params={'indent': 4})
|
||||
@ -565,9 +493,7 @@ def test(request):
|
||||
from cookbook.helper.ingredient_parser import IngredientParser
|
||||
parser = IngredientParser(request, False)
|
||||
|
||||
data = {
|
||||
'original': '90g golden syrup'
|
||||
}
|
||||
data = {'original': '90g golden syrup'}
|
||||
data['parsed'] = parser.parse(data['original'])
|
||||
|
||||
return render(request, 'test.html', {'data': data})
|
||||
|
Loading…
Reference in New Issue
Block a user