diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..2369d68 --- /dev/null +++ b/404.html @@ -0,0 +1,415 @@ + + + +
+ + + + + + + + + + + + + +This decorator transform any ModelAdmin method to a view and add a button to the Admin objects toolbar.
+Examples:
+
+from admin_extra_buttons.api import ExtraButtonsMixin, button
+
+
+class MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):
+ @button()
+ def refresh_all(self, request):
+ # your business logic here
+ ...
+ self.message_user(request, 'refresh called')
+ # do not return HttpResponse(), so user will be redirected to the original page
+
+ @button()
+ def scan(self, request):
+ return HttpResponse("Done") # return specific response
+
+ @button()
+ def scan(self, request):
+ if request.method == 'POST':
+ ....
+ else:
+ return TemplateResponse()
+
+
+Note
+AEB try to understand if a button should appear in the change_form
and/or in the change_list
page.
+If the decorated method has only one argument (es. def scan(self, request)
), the button will only be visible
+on the change_list
page, if it contains more that one argumente (es. def scan(self, request, pk)
)
+the button will be visible in the change_form
page.
None
True
do show the button on the change_form
page
+ If set to None
(default), use method signature to display the button None
True
do show the button on the change_list
page
+If set to None
(default), use method signature to display the buttonTrue
True
True
{}
decorated method name
True
Note
+id
is automacally set if not provided,
+class
is updated/set based on disable_on_click
and disable_on_edit
values
decorated method name
<function_name>/<path:arg1>/<path:arg2>/....
None
Simplest usage. Display a button and create a view on admin/mymodel/scan
.
@register(MyModel)
+class MyModelAdmin(ExtrButtonsMixi, admin.ModelAdmin):
+
+ @button()
+ def scan(self, request):
+ pass
+
+Buttons with custom permission, one for change_list
and other for change_form
@register(MyModel)
+class MyModelAdmin(ExtrButtonsMixi, admin.ModelAdmin):
+
+ @button(permission=lambda request, obj: request.user.is_superuser)
+ def delete_all(self, request):
+ pass
+
+ @button(permission='app.delete_mymodel)
+ def mark(self, request, pk):
+ obj = self.get_object(request.pk)
+ obj.mark = True
+ obj.save()
+
+Buttons with custom permission, one for change_list
and other for change_form
@register(MyModel)
+class MyModelAdmin(ExtrButtonsMixi, admin.ModelAdmin):
+
+ @button(permission=lambda request, obj: request.user.is_superuser,
+ html_attrs={'style': 'background-color:var(--button-bg)'},
+ label=_('Delete All Records'),
+ change_form=True
+ )
+ def delete_all(self, request):
+ pass
+
+ @button(permission=lambda request, obj: request.user.is_superuser,
+ html_attrs={'style': 'background-color:var(--button-bg)'},
+ enabled=lambda btn: btn.original.status == SUCCESS,
+ label=_('Delete All Records'),
+ change_form=True
+ )
+ def toggle(self, request, pk):
+ pass
+
+
+
+
+
+
+
+ This decorator allows "grouping" different @view()
decorated methods under the same HTML <select>
Examples:
+from admin_extra_buttons.api import ExtraButtonsMixin, choice, view
+
+class MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):
+ @choice(change_list=True)
+ def menu1(self, button):
+ button.choices = [self.test1, self.test2]
+
+ @view()
+ def test1(self, request):
+ self.message_user(request, "You have selected test1")
+
+ @view()
+ def test2(self, request, pk):
+ context = self.get_common_context(request, pk)
+ self.message_user(request, f"You have selected test22 on {context['original']}")
+ return TemplateResponse(request, "demo/test22.html", context)
+
+True
change_form
pageTrue
change_list
pageTrue
{}
decorated method name
True
class MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):
+
+ @choice(label="Menu #1",
+ change_list=False,
+ html_attrs={'target': '_new', 'style': 'background-color:var(--button-bg)'})
+ def menu1(self, button):
+ original = button.original
+ button.label = f"Search '{original.name}' on Google"
+ if button.requst.user.is_superuser:
+ button.choices = [self.feat1, self.feat2, self.feat3, self.feat4]
+ else:
+ button.choices = [self.feat1, self.feat2]
+
+ @view()
+ def feat1(self, request):
+ self.message_user(request, "You have selected Feature #1")
+
+ @view()
+ def feat2(self, request):
+ return TemplateResponse(request, "demo/feat2.html", context)
+
+ @view(permission=lambda request, obj: request.user.is_superuser)
+ def feat3(self, request):
+ return HttpResponse("You have selected Feature #3")
+
+ @view(permission=lambda request, obj: request.user.is_superuser)
+ def feat3(self, request):
+ self.message_user(request, "You have selected Feature #3")
+
+
+
+
+
+
+
+ Warning
+This is an internal API and should be used only for extend/customise core behaviours.
+View handler for @button
decorated views
View handler for @link
decorated views
View handler for @view
decorated views
Button class for Django views based buttons
+Button class for links based buttons (buttons not linked to Django views)
+Button class for choices buttons
+ + + + + + +Use this decorator if you want to create links to external +resources or if you already have the required view.
+Note
+@link() buttons by defaults are visible both on change_list
and change_form
pages
Examples:
+from admin_extra_buttons.api import ExtraButtonsMixin, link
+
+class MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):
+ @link(href="https://www.google.com/", change_form=False)
+ def google(self, button):
+ pass
+
+ @link(href=None, change_list=False)
+ def search_on_google(self, button):
+ button.label = f"Search '{button.original.name}' on Google"
+ button.href = f"https://www.google.com/?q={button.original.name}"
+
+True
change_form
pageTrue
change_list
page"""
href
attribute value True
{}
decorated method name
True
class MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):
+
+ @link(href=None, change_list=False)
+ def search_on_google(self, button):
+ button.label = f"Search '{button.original.name}' on Google"
+ button.href = f"https://www.google.com/?q={original.name}"
+
+class MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):
+
+@link(href=None,
+ change_list=False,
+ html_attrs={'target': '_new', 'style': 'background-color:var(--button-bg)'})
+def search_on_google(self, button):
+ button.label = f"Search '{button.original.name}' on Google"
+ button.href = f"https://www.google.com/?q={button.original.name}"
+
+
+
+
+
+
+
+ Mixin to use with ModelAdmin to properly handle button decorators
+admin_extra_buttons/change_list.html
admin_extra_buttons/change_form.html
change_form=True
or change_form=None
change_list=True
or change_list=None
Use this decorator to add views to any ModelAdmin. This decorator will not create any button.
+<function_name>/<path:arg1>/<path:arg2>/....
None
True
False
@register(MyModel)
+class MyModelAdmin(ExtrButtonsMixi, admin.ModelAdmin):
+
+ @view()
+ def sele(self, request):
+
+@register(MyModel)
+class MyModelAdmin(ExtrButtonsMixi, admin.ModelAdmin):
+
+ @view(http_basic_auth=True)
+ def api4(self, request):
+ return HttpResponse("Basic Authentication allowed")
+
+
+
+
+
+
+
+ This example shows how to create a button that display a form to upload a file and process it.
+admin_extra_buttons/upload.html
{% extends "admin_extra_buttons/action_page.html" %}
+{% load i18n static admin_list admin_urls %}
+
+{% block action-content %}
+ <form method="post" enctype="multipart/form-data">
+ {% csrf_token %}
+ {{ form.as_p }}
+ <button type="submit">Upload</button>
+ </form>
+
+{% endblock %}
+
+admin.py
class UploadForm(forms.Form):
+ docfile = forms.FileField( label='Select a file')
+
+class MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):
+
+ @button()
+ def upload(self, request):
+ context = self.get_common_context(request, title='Upload')
+ if request.method == 'POST':
+ form = UploadForm(request.POST, request.FILES)
+ if form.is_valid():
+ downloaded_file = request.FILES['docfile']
+ # process file
+ ...
+ ...
+ return redirect(admin_urlname(context['opts'], 'changelist') )
+ else:
+ form = UploadForm()
+ context['form'] = form
+ return TemplateResponse(request, 'admin_extra_buttons/upload.html', context)
+
+
+
+
+
+
+
+ This is a full rewriting of the original django-admin-extra-url
. It
+provides decorators to easily add custom buttons to Django Admin pages and/or add views to any ModelAdmin
It allows easy creation of wizards, actions and/or links to external resources +as well as api only views.
+Three decorators are available:
+button()
to mark a method as extra view and show related buttonlink()
This is used for "external" link, where you don't need to invoke local views.view()
View only decorator, this adds a new url but do not render any button.choice()
Menu like button, can be used to group multiple @views().pip install django-admin-extra-buttons
+
+After installation add it to INSTALLED_APPS
INSTALLED_APPS = (
+ ...
+ 'admin_extra_buttons',
+)
+
+
+from admin_extra_buttons.api import ExtraButtonsMixin, button, confirm_action, link, view
+from admin_extra_buttons.utils import HttpResponseRedirectToReferrer
+from django.http import HttpResponse, JsonResponse
+from django.contrib import admin
+from django.views.decorators.clickjacking import xframe_options_sameorigin
+from django.views.decorators.csrf import csrf_exempt
+
+class MyModelModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):
+
+ @button(permission='demo.add_demomodel1',
+ change_form=True,
+ html_attrs={'style': 'background-color:#88FF88;color:black'})
+ def refresh(self, request):
+ self.message_user(request, 'refresh called')
+ # Optional: returns HttpResponse
+ return HttpResponseRedirectToReferrer(request)
+
+ @button(html_attrs={'style': 'background-color:#DC6C6C;color:black'})
+ def confirm(self, request):
+ def _action(request):
+ pass
+
+ return confirm_action(self, request, _action, "Confirm action",
+ "Successfully executed", )
+
+ @link(href=None,
+ change_list=False,
+ html_attrs={'target': '_new', 'style': 'background-color:var(--button-bg)'})
+ def search_on_google(self, button):
+ original = button.context['original']
+ button.label = f"Search '{original.name}' on Google"
+ button.href = f"https://www.google.com/?q={original.name}"
+
+ @view()
+ def select2_autocomplete(self, request):
+ return JsonResponse({})
+
+ @view(http_basic_auth=True)
+ def api4(self, request):
+ return HttpResponse("Basic Authentication allowed")
+
+ @view(decorators=[csrf_exempt, xframe_options_sameorigin])
+ def preview(self, request):
+ if request.method == "POST":
+ return HttpResponse("POST")
+ return HttpResponse("GET")
+
+
+
+This is a full rewriting of the original django-admin-extra-url
. It provides decorators to easily add custom buttons to Django Admin pages and/or add views to any ModelAdmin
It allows easy creation of wizards, actions and/or links to external resources as well as api only views.
Three decorators are available:
button()
to mark a method as extra view and show related buttonlink()
This is used for \"external\" link, where you don't need to invoke local views.view()
View only decorator, this adds a new url but do not render any button.choice()
Menu like button, can be used to group multiple @views().pip install django-admin-extra-buttons\n
After installation add it to INSTALLED_APPS
INSTALLED_APPS = (\n ...\n 'admin_extra_buttons',\n)\n
"},{"location":"#how-to-use-it","title":"How to use it","text":"\nfrom admin_extra_buttons.api import ExtraButtonsMixin, button, confirm_action, link, view\nfrom admin_extra_buttons.utils import HttpResponseRedirectToReferrer\nfrom django.http import HttpResponse, JsonResponse\nfrom django.contrib import admin\nfrom django.views.decorators.clickjacking import xframe_options_sameorigin\nfrom django.views.decorators.csrf import csrf_exempt\n\nclass MyModelModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):\n\n @button(permission='demo.add_demomodel1',\n change_form=True,\n html_attrs={'style': 'background-color:#88FF88;color:black'})\n def refresh(self, request):\n self.message_user(request, 'refresh called')\n # Optional: returns HttpResponse\n return HttpResponseRedirectToReferrer(request)\n\n @button(html_attrs={'style': 'background-color:#DC6C6C;color:black'})\n def confirm(self, request):\n def _action(request):\n pass\n\n return confirm_action(self, request, _action, \"Confirm action\",\n \"Successfully executed\", )\n\n @link(href=None, \n change_list=False, \n html_attrs={'target': '_new', 'style': 'background-color:var(--button-bg)'})\n def search_on_google(self, button):\n original = button.context['original']\n button.label = f\"Search '{original.name}' on Google\"\n button.href = f\"https://www.google.com/?q={original.name}\"\n\n @view()\n def select2_autocomplete(self, request):\n return JsonResponse({})\n\n @view(http_basic_auth=True)\n def api4(self, request):\n return HttpResponse(\"Basic Authentication allowed\")\n\n @view(decorators=[csrf_exempt, xframe_options_sameorigin])\n def preview(self, request):\n if request.method == \"POST\":\n return HttpResponse(\"POST\")\n return HttpResponse(\"GET\")\n\n\n
"},{"location":"#project-links","title":"Project Links","text":"This example shows how to create a button that display a form to upload a file and process it.
admin_extra_buttons/upload.html
{% extends \"admin_extra_buttons/action_page.html\" %}\n{% load i18n static admin_list admin_urls %}\n\n{% block action-content %}\n <form method=\"post\" enctype=\"multipart/form-data\">\n {% csrf_token %}\n {{ form.as_p }}\n <button type=\"submit\">Upload</button>\n </form>\n\n{% endblock %}\n
admin.py
class UploadForm(forms.Form):\n docfile = forms.FileField( label='Select a file')\n\nclass MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):\n\n @button()\n def upload(self, request):\n context = self.get_common_context(request, title='Upload')\n if request.method == 'POST':\n form = UploadForm(request.POST, request.FILES)\n if form.is_valid():\n downloaded_file = request.FILES['docfile']\n # process file\n ...\n ...\n return redirect(admin_urlname(context['opts'], 'changelist') )\n else:\n form = UploadForm()\n context['form'] = form\n return TemplateResponse(request, 'admin_extra_buttons/upload.html', context)\n
"},{"location":"api/button/","title":"@button()","text":"This decorator transform any ModelAdmin method to a view and add a button to the Admin objects toolbar.
Examples:
\nfrom admin_extra_buttons.api import ExtraButtonsMixin, button\n\n\nclass MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):\n @button() \n def refresh_all(self, request):\n # your business logic here\n ...\n self.message_user(request, 'refresh called')\n # do not return HttpResponse(), so user will be redirected to the original page\n\n @button()\n def scan(self, request):\n return HttpResponse(\"Done\") # return specific response\n\n @button()\n def scan(self, request):\n if request.method == 'POST':\n ....\n else:\n return TemplateResponse()\n\n
Note
AEB try to understand if a button should appear in the change_form
and/or in the change_list
page. If the decorated method has only one argument (es. def scan(self, request)
), the button will only be visible on the change_list
page, if it contains more that one argumente (es. def scan(self, request, pk)
) the button will be visible in the change_form
page.
None
set to True
do show the button on the change_form
page If set to None
(default), use method signature to display the button change_list: None
set to True
do show the button on the change_list
page If set to None
(default), use method signature to display the button disable_on_click: True
automatically disable button on click() to prevent unintentional double processing disable_on_edit: True
automatically disable button when any FORM in page is modified enabled: True
bool or callable to set enable status html_attrs: {}
Dictionary of html tags to use in button rendering. label: decorated method name
button label visible: True
bool or callable show/hide button Note
id
is automacally set if not provided, class
is updated/set based on disable_on_click
and disable_on_edit
values
decorated method name
button label pattern: <function_name>/<path:arg1>/<path:arg2>/....
url pattern to use for the url genaration. permission: None
Django permission code needed to access the view and display the button"},{"location":"api/button/#examples","title":"Examples","text":""},{"location":"api/button/#simple","title":"Simple","text":"Simplest usage. Display a button and create a view on admin/mymodel/scan
.
@register(MyModel)\nclass MyModelAdmin(ExtrButtonsMixi, admin.ModelAdmin):\n\n @button()\n def scan(self, request):\n pass\n
"},{"location":"api/button/#check-permissions","title":"Check Permissions","text":"Buttons with custom permission, one for change_list
and other for change_form
@register(MyModel)\nclass MyModelAdmin(ExtrButtonsMixi, admin.ModelAdmin):\n\n @button(permission=lambda request, obj: request.user.is_superuser)\n def delete_all(self, request):\n pass\n\n @button(permission='app.delete_mymodel)\n def mark(self, request, pk):\n obj = self.get_object(request.pk)\n obj.mark = True\n obj.save()\n
"},{"location":"api/button/#fully-featured","title":"Fully featured","text":"Buttons with custom permission, one for change_list
and other for change_form
@register(MyModel)\nclass MyModelAdmin(ExtrButtonsMixi, admin.ModelAdmin):\n\n @button(permission=lambda request, obj: request.user.is_superuser,\n html_attrs={'style': 'background-color:var(--button-bg)'},\n label=_('Delete All Records'),\n change_form=True\n )\n def delete_all(self, request):\n pass\n\n @button(permission=lambda request, obj: request.user.is_superuser,\n html_attrs={'style': 'background-color:var(--button-bg)'},\n enabled=lambda btn: btn.original.status == SUCCESS,\n label=_('Delete All Records'),\n change_form=True\n )\n def toggle(self, request, pk):\n pass\n
"},{"location":"api/choice/","title":"@choice()","text":"This decorator allows \"grouping\" different @view()
decorated methods under the same HTML <select>
Examples:
from admin_extra_buttons.api import ExtraButtonsMixin, choice, view\n\nclass MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):\n @choice(change_list=True)\n def menu1(self, button):\n button.choices = [self.test1, self.test2]\n\n @view()\n def test1(self, request):\n self.message_user(request, \"You have selected test1\")\n\n @view()\n def test2(self, request, pk):\n context = self.get_common_context(request, pk)\n self.message_user(request, f\"You have selected test22 on {context['original']}\")\n return TemplateResponse(request, \"demo/test22.html\", context)\n
"},{"location":"api/choice/#options","title":"Options","text":"change_form: True
display the button on the change_form
page change_list: True
display the button on the change_list
page enabled: True
bool or callable to set enable status html_attrs: {}
Dictionary of html tags to use in button rendering label: decorated method name
button label visible: True
bool or callable show/hide button"},{"location":"api/choice/#attributes","title":"Attributes","text":"context TemplateContext from the Django template as at the moment of rendering"},{"location":"api/choice/#examples","title":"Examples","text":""},{"location":"api/choice/#complex-configuration","title":"Complex Configuration","text":"class MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):\n\n @choice(label=\"Menu #1\",\n change_list=False,\n html_attrs={'target': '_new', 'style': 'background-color:var(--button-bg)'})\n def menu1(self, button):\n original = button.original\n button.label = f\"Search '{original.name}' on Google\"\n if button.requst.user.is_superuser: \n button.choices = [self.feat1, self.feat2, self.feat3, self.feat4]\n else:\n button.choices = [self.feat1, self.feat2]\n\n @view()\n def feat1(self, request):\n self.message_user(request, \"You have selected Feature #1\")\n\n @view()\n def feat2(self, request):\n return TemplateResponse(request, \"demo/feat2.html\", context)\n\n @view(permission=lambda request, obj: request.user.is_superuser)\n def feat3(self, request):\n return HttpResponse(\"You have selected Feature #3\")\n\n @view(permission=lambda request, obj: request.user.is_superuser)\n def feat3(self, request):\n self.message_user(request, \"You have selected Feature #3\")\n
"},{"location":"api/handlers/","title":"Handlers","text":"Warning
This is an internal API and should be used only for extend/customise core behaviours.
"},{"location":"api/handlers/#buttonhandler","title":"ButtonHandler","text":"View handler for @button
decorated views
View handler for @link
decorated views
View handler for @view
decorated views
Button class for Django views based buttons
"},{"location":"api/handlers/#linkbutton","title":"LinkButton","text":"Button class for links based buttons (buttons not linked to Django views)
"},{"location":"api/handlers/#choicebutton","title":"ChoiceButton","text":"Button class for choices buttons
"},{"location":"api/link/","title":"@link()","text":"Use this decorator if you want to create links to external resources or if you already have the required view.
Note
@link() buttons by defaults are visible both on change_list
and change_form
pages
Examples:
from admin_extra_buttons.api import ExtraButtonsMixin, link\n\nclass MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):\n @link(href=\"https://www.google.com/\", change_form=False)\n def google(self, button):\n pass\n\n @link(href=None, change_list=False)\n def search_on_google(self, button):\n button.label = f\"Search '{button.original.name}' on Google\"\n button.href = f\"https://www.google.com/?q={button.original.name}\"\n
"},{"location":"api/link/#options","title":"Options","text":"change_form: True
display the button on the change_form
page change_list: True
display the button on the change_list
page href: \"\"\"
HTML href
attribute value enabled: True
bool or callable to set enable status html_attrs: {}
Dictionary of html tags to use in button rendering label: decorated method name
button label visible: True
bool or callable show/hide button"},{"location":"api/link/#attributes","title":"Attributes","text":"context TemplateContext from the Django template as at the moment of rendering"},{"location":"api/link/#examples","title":"Examples","text":""},{"location":"api/link/#dynamic-configuration","title":"Dynamic Configuration","text":"class MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):\n\n @link(href=None, change_list=False)\n def search_on_google(self, button):\n button.label = f\"Search '{button.original.name}' on Google\"\n button.href = f\"https://www.google.com/?q={original.name}\"\n
"},{"location":"api/link/#fully-featured","title":"Fully featured","text":"class MyModelAdmin(ExtraButtonsMixin, admin.ModelAdmin):\n\n@link(href=None, \n change_list=False, \n html_attrs={'target': '_new', 'style': 'background-color:var(--button-bg)'})\ndef search_on_google(self, button):\n button.label = f\"Search '{button.original.name}' on Google\"\n button.href = f\"https://www.google.com/?q={button.original.name}\"\n
"},{"location":"api/mixin/","title":"ExtraButtonMixin","text":"Mixin to use with ModelAdmin to properly handle button decorators
"},{"location":"api/mixin/#attributes","title":"Attributes","text":"change_list_template Defaultadmin_extra_buttons/change_list.html
change_form_template Default admin_extra_buttons/change_form.html
"},{"location":"api/mixin/#methods","title":"Methods","text":"get_changeform_buttons(context) Return the list of buttons that will be displayed on the change form page. Default implementation returns all the buttons with change_form=True
or change_form=None
get_changelist_buttons(context) Return the list of buttons that will be displayed on the changelist page. Default implementation returns all the buttons with change_list=True
or change_list=None
get_action_buttons(context) Return the list of buttons that will be displayed on the extra action page. get_common_context() This method returns a django template Context filled with the common values that can be useful when create custom views that render templates. (@see Build two steps action) message_error_to_user() Shortcut to display message on Exception"},{"location":"api/view/","title":"@view()","text":"Use this decorator to add views to any ModelAdmin. This decorator will not create any button.
"},{"location":"api/view/#options","title":"Options","text":"pattern:<function_name>/<path:arg1>/<path:arg2>/....
url pattern to use for the url generation. permission: None
Django permission code needed to access the view and display the button. Can be a callable login_required: True
Set to False to allow access to anonymous users http_basic_auth: False
Enable Basic Authentication for this view"},{"location":"api/view/#examples","title":"Examples","text":""},{"location":"api/view/#simple","title":"Simple","text":"@register(MyModel)\nclass MyModelAdmin(ExtrButtonsMixi, admin.ModelAdmin):\n\n @view()\n def sele(self, request):\n
"},{"location":"api/view/#http-basic-authentication","title":"HTTP Basic Authentication","text":"@register(MyModel)\nclass MyModelAdmin(ExtrButtonsMixi, admin.ModelAdmin):\n\n @view(http_basic_auth=True)\n def api4(self, request):\n return HttpResponse(\"Basic Authentication allowed\")\n
"}]}
\ No newline at end of file
diff --git a/sitemap.xml b/sitemap.xml
new file mode 100644
index 0000000..a56705f
--- /dev/null
+++ b/sitemap.xml
@@ -0,0 +1,48 @@
+
+