class OrderModule(AdminModule): name = _("Orders") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:order.list") def get_urls(self): return [ admin_url( r"^orders/(?P<pk>\d+)/create-shipment/(?P<supplier_pk>\d+)/$", "shuup.admin.modules.orders.views.OrderCreateShipmentView", name="order.create-shipment", ), admin_url( r"^shipments/(?P<pk>\d+)/delete/$", "shuup.admin.modules.orders.views.ShipmentDeleteView", name="order.delete-shipment", ), admin_url( r"^shipments/(?P<pk>\d+)/set-sent/$", "shuup.admin.modules.orders.views.ShipmentSetSentView", name="order.set-shipment-sent", ), admin_url(r"^shipments/$", "shuup.admin.modules.orders.views.ShipmentListView", name="order.shipments.list"), admin_url( r"^orders/(?P<pk>\d+)/create-payment/$", "shuup.admin.modules.orders.views.OrderCreatePaymentView", name="order.create-payment", ), admin_url( r"^orders/(?P<pk>\d+)/delete-payment/$", "shuup.admin.modules.orders.views.OrderDeletePaymentView", name="order.delete-payment", ), admin_url( r"^orders/(?P<pk>\d+)/set-paid/$", "shuup.admin.modules.orders.views.OrderSetPaidView", name="order.set-paid", ), admin_url( r"^orders/(?P<pk>\d+)/set-status/$", "shuup.admin.modules.orders.views.OrderSetStatusView", name="order.set-status", ), admin_url( r"^orders/(?P<pk>\d+)/new-log-entry/$", "shuup.admin.modules.orders.views.NewLogEntryView", name="order.new-log-entry", ), admin_url( r"^orders/(?P<pk>\d+)/update-admin-comment/$", "shuup.admin.modules.orders.views.UpdateAdminCommentView", name="order.update-admin-comment", ), admin_url( r"^orders/(?P<pk>\d+)/create-refund/$", "shuup.admin.modules.orders.views.OrderCreateRefundView", name="order.create-refund", ), admin_url( r"^orders/(?P<pk>\d+)/create-refund/full-refund$", "shuup.admin.modules.orders.views.OrderCreateFullRefundView", name="order.create-full-refund", ), admin_url(r"^orders/(?P<pk>\d+)/$", "shuup.admin.modules.orders.views.OrderDetailView", name="order.detail"), admin_url(r"^orders/new/$", "shuup.admin.modules.orders.views.OrderEditView", name="order.new"), admin_url(r"^orders/(?P<pk>\d+)/edit/$", "shuup.admin.modules.orders.views.OrderEditView", name="order.edit"), admin_url(r"^orders/$", "shuup.admin.modules.orders.views.OrderListView", name="order.list"), admin_url( r"^orders/list-settings/", "shuup.admin.modules.settings.views.ListSettingsView", name="order.list_settings", ), admin_url( r"^orders/(?P<pk>\d+)/edit-addresses/$", "shuup.admin.modules.orders.views.OrderAddressEditView", name="order.edit-addresses", ), ] def get_menu_entries(self, request): return [ MenuEntry( text=_("Orders"), icon="fa fa-inbox", url="shuup_admin:order.list", category=ORDERS_MENU_CATEGORY, ordering=1, aliases=[_("Show orders")], ), MenuEntry( text=_("Shipments"), icon="fa fa-truck", url="shuup_admin:order.shipments.list", category=ORDERS_MENU_CATEGORY, ordering=2, aliases=[_("Show shipments")], ), ] def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: orders = Order.objects.filter( Q(identifier__istartswith=query) | Q(reference_number__istartswith=query) | Q(email__icontains=query) | Q(phone__icontains=query)).order_by("-id")[:15] for i, order in enumerate(orders): relevance = 100 - i yield SearchResult(text=six.text_type(order), url=get_model_url(order), category=_("Orders"), relevance=relevance) def get_notifications(self, request): shop = request.shop old_open_orders = Order.objects.filter( shop=shop, status__role=OrderStatusRole.INITIAL, order_date__lt=now() - timedelta(days=4)).count() if old_open_orders: yield Notification(title=_("Outstanding Orders"), text=_("%d outstanding orders") % old_open_orders, kind="danger") def get_model_url(self, object, kind, shop=None): return derive_model_url(Order, "shuup_admin:order", object, kind) def get_help_blocks(self, request, kind): from shuup.admin.utils.permissions import has_permission if kind == "quicklink" and has_permission(request.user, "order.new"): actions = [{ "text": _("New order"), "url": self.get_model_url(Order, "new") }] yield SimpleHelpBlock( text=_("New order"), actions=actions, icon_url="shuup_admin/img/product.png", priority=0, category=HelpBlockCategory.ORDERS, done=Order.objects.filter( shop=request.shop).exists() if kind == "setup" else False, )
class ProductModule(AdminModule): name = _("Products") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:product.list") def get_urls(self): return [ admin_url( "^products/(?P<pk>\d+)/delete/$", "shuup.admin.modules.products.views.ProductDeleteView", name="product.delete", permissions=["shuup.delete_product"] ), admin_url( "^products/(?P<pk>\d+)/media/$", "shuup.admin.modules.products.views.ProductMediaEditView", name="product.edit_media", permissions=get_default_model_permissions(Product), ), admin_url( "^products/(?P<pk>\d+)/crosssell/$", "shuup.admin.modules.products.views.ProductCrossSellEditView", name="product.edit_cross_sell", permissions=get_default_model_permissions(ProductCrossSell), ), admin_url( "^products/(?P<pk>\d+)/variation/$", "shuup.admin.modules.products.views.ProductVariationView", name="product.edit_variation", permissions=get_default_model_permissions(ProductVariationResult), ), admin_url( "^products/(?P<pk>\d+)/package/$", "shuup.admin.modules.products.views.ProductPackageView", name="product.edit_package", permissions=get_default_model_permissions(ProductPackageLink), ), ] + get_edit_and_list_urls( url_prefix="^products", view_template="shuup.admin.modules.products.views.Product%sView", name_template="product.%s", permissions=get_default_model_permissions(Product), ) def get_menu_category_icons(self): return {self.name: "fa fa-cube"} def get_menu_entries(self, request): category = _("Products") return [ MenuEntry( text=_("Products"), icon="fa fa-cube", url="shuup_admin:product.list", category=category ) ] def get_search_results(self, request, query): minimum_query_length = 3 skus_seen = set() if len(query) >= minimum_query_length: pk_counter = Counter() pk_counter.update(Product.objects.filter(sku__startswith=query).values_list("pk", flat=True)) name_q = Q() for part in split_query(query, minimum_query_length): name_q &= Q(name__icontains=part) pk_counter.update( Product._parler_meta.root_model.objects.filter(name_q).values_list("master_id", flat=True) ) pks = [pk for (pk, count) in pk_counter.most_common(10)] for product in Product.objects.filter(pk__in=pks): relevance = 100 - pk_counter.get(product.pk, 0) skus_seen.add(product.sku.lower()) yield SearchResult( text=force_text(product), url=get_model_url(product), category=_("Products"), relevance=relevance ) if len(query) >= minimum_query_length: url = reverse("shuup_admin:product.new") if " " in query: yield SearchResult( text=_("Create Product Called \"%s\"") % query, url=manipulate_query_string(url, name=query), is_action=True ) else: if query.lower() not in skus_seen: yield SearchResult( text=_("Create Product with SKU \"%s\"") % query, url=manipulate_query_string(url, sku=query), is_action=True ) def get_required_permissions(self): return get_permissions_from_urls(self.get_urls()) | get_default_model_permissions(Product) def get_model_url(self, object, kind): return derive_model_url(Product, "shuup_admin:product", object, kind)
class ShopModule(AdminModule): name = _("Shops") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:shop.list") def get_urls(self): return [ admin_url("^shops/(?P<pk>\d+)/enable/$", "shuup.admin.modules.shops.views.ShopEnablerView", name="shop.enable"), admin_url("^shops/(?P<pk>\d+)/select/$", "shuup.admin.modules.shops.views.ShopSelectView", name="shop.select"), ] + get_edit_and_list_urls( url_prefix="^shops", view_template="shuup.admin.modules.shops.views.Shop%sView", name_template="shop.%s") def get_menu_entries(self, request): return [ MenuEntry(text=self.name, icon="fa fa-home", url="shuup_admin:shop.list", category=STOREFRONT_MENU_CATEGORY, ordering=4), ] def get_help_blocks(self, request, kind): if kind == "setup": shop = request.shop yield SimpleHelpBlock( text=_("Add a logo to make your store stand out"), actions=[{ "text": _("Add logo"), "url": self.get_model_url(shop, "edit"), "hash": "#shop-images-section" }], icon_url="shuup_admin/img/logo_icon.svg", done=shop.logo, required=False) shop = get_shop(request) yield SimpleHelpBlock( priority=1000, text=_("Publish your store"), description=_( "Let customers browse your store and make purchases"), css_class="green ", actions=[{ "method": "POST", "text": _("Publish shop"), "url": reverse("shuup_admin:shop.enable", kwargs={"pk": shop.pk}), "data": { "enable": True, "redirect": reverse("shuup_admin:dashboard") } }], icon_url="shuup_admin/img/publish.png", done=(not shop.maintenance_mode), required=False) def get_model_url(self, object, kind, shop=None): return derive_model_url(Shop, "shuup_admin:shop", object, kind) def get_search_results(self, request, query): if not settings.SHUUP_ENABLE_MULTIPLE_SHOPS: return minimum_query_length = 3 if len(query) >= minimum_query_length: shops = Shop.objects.get_for_user(request.user).filter( translations__name__icontains=query, status=ShopStatus.ENABLED) for i, shop in enumerate(shops[:10]): relevance = 100 - i yield SearchResult( text=(_('Set "{}" as the active shop')).format(shop.name), url=get_model_url(shop, "select"), category=( _("Available Shops [currently active: {}]")).format( request.shop.name), relevance=relevance)
class ContactModule(AdminModule): name = _("Contacts") breadcrumbs_menu_entry = MenuEntry(text=name, url="shuup_admin:contact.list", category=CONTACTS_MENU_CATEGORY) def get_urls(self): return [ admin_url( "^contacts/new/$", "shuup.admin.modules.contacts.views.ContactEditView", kwargs={"pk": None}, name="contact.new", permissions=["shuup.add_contact"], ), admin_url( "^contacts/(?P<pk>\d+)/edit/$", "shuup.admin.modules.contacts.views.ContactEditView", name="contact.edit", permissions=["shuup.change_contact"], ), admin_url( "^contacts/(?P<pk>\d+)/$", "shuup.admin.modules.contacts.views.ContactDetailView", name="contact.detail", permissions=get_default_model_permissions(Contact), ), admin_url( "^contacts/reset-password/(?P<pk>\d+)/$", "shuup.admin.modules.contacts.views.ContactResetPasswordView", name="contact.reset_password", permissions=get_default_model_permissions(Contact), ), admin_url( "^contacts/$", "shuup.admin.modules.contacts.views.ContactListView", name="contact.list", permissions=get_default_model_permissions(Contact), ), admin_url( "^contacts/list-settings/", "shuup.admin.modules.settings.views.ListSettingsView", name="contact.list_settings", permissions=get_default_model_permissions(Contact), ) ] def get_menu_entries(self, request): return [ MenuEntry( text=_("Contacts"), icon="fa fa-users", url="shuup_admin:contact.list", category=CONTACTS_MENU_CATEGORY, ordering=1 ) ] def get_required_permissions(self): return ( get_default_model_permissions(CompanyContact) | get_default_model_permissions(Contact) | get_default_model_permissions(PersonContact) ) def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: contacts = Contact.objects.filter( Q(name__icontains=query) | Q(email=query) ) for i, contact in enumerate(contacts[:10]): relevance = 100 - i yield SearchResult( text=six.text_type(contact), url=get_model_url(contact), category=_("Contacts"), relevance=relevance ) def get_model_url(self, object, kind): return derive_model_url(Contact, "shuup_admin:contact", object, kind)
class ImportAdminModule(AdminModule): name = _("Data Import") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:importer.import") def get_extra_permissions(self): extra_permissions = ["importer.show-all-imports"] importers_permissions = [importer.get_permission_identifier() for importer in get_provide_objects("importers")] return extra_permissions + importers_permissions def get_permissions_help_texts(self): help_texts = { "importer.show-all-imports": _("Allow the user to see the imports from all shops and suppliers."), "importer.import_process": _("Allow the user to run importers."), "importer.import": _("Allow the user to list imports."), "importer.import.new": _("Allow the user to import a file."), "importer.download_example": _("Allow the user to download sample files."), } for importer in get_provide_objects("importers"): help_texts[importer.get_permission_identifier()] = _( "Allow the user to use the {importer_name} importer." ).format(importer_name=importer.name) return help_texts def get_urls(self): return [ admin_url( "^importer/imports/$", "shuup.importer.admin_module.import_views.ImportListView", name="importer.import" ), admin_url( r"^importer/imports/(?P<pk>.+)/$", "shuup.importer.admin_module.import_views.ImportDetailView", name="importer.import.detail", ), admin_url( "^importer/new/$", "shuup.importer.admin_module.import_views.ImportView", name="importer.import.new", ), admin_url( "^importer/process$", "shuup.importer.admin_module.import_views.ImportProcessView", name="importer.import_process", ), admin_url( "^importer/example$", "shuup.importer.admin_module.import_views.ExampleFileDownloadView", name="importer.download_example", ), ] def get_menu_entries(self, request): return [ MenuEntry( text=_("Data Import"), category=SETTINGS_MENU_CATEGORY, url="shuup_admin:importer.import", icon="fa fa-star", ) ]
class OrderModule(AdminModule): name = _("Orders") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:order.list") def get_urls(self): return [ admin_url( "^orders/(?P<pk>\d+)/create-shipment/$", "shuup.admin.modules.orders.views.OrderCreateShipmentView", name="order.create-shipment", permissions=["shuup.add_shipment"]), admin_url("^shipments/(?P<pk>\d+)/delete/$", "shuup.admin.modules.orders.views.ShipmentDeleteView", name="order.delete-shipment", permissions=["shuup.delete_shipment"]), admin_url( "^orders/(?P<pk>\d+)/create-payment/$", "shuup.admin.modules.orders.views.OrderCreatePaymentView", name="order.create-payment", permissions=["shuup.add_payment"]), admin_url("^orders/(?P<pk>\d+)/set-paid/$", "shuup.admin.modules.orders.views.OrderSetPaidView", name="order.set-paid", permissions=["shuup.add_payment"]), admin_url("^orders/(?P<pk>\d+)/set-status/$", "shuup.admin.modules.orders.views.OrderSetStatusView", name="order.set-status", permissions=get_default_model_permissions(Order)), admin_url("^orders/(?P<pk>\d+)/new-log-entry/$", "shuup.admin.modules.orders.views.NewLogEntryView", name="order.new-log-entry", permissions=get_default_model_permissions(Order)), admin_url( "^orders/(?P<pk>\d+)/update-admin-comment/$", "shuup.admin.modules.orders.views.UpdateAdminCommentView", name="order.update-admin-comment", permissions=get_default_model_permissions(Order)), admin_url("^orders/(?P<pk>\d+)/create-refund/$", "shuup.admin.modules.orders.views.OrderCreateRefundView", name="order.create-refund", permissions=get_default_model_permissions(Order)), admin_url( "^orders/(?P<pk>\d+)/create-refund/full-refund$", "shuup.admin.modules.orders.views.OrderCreateFullRefundView", name="order.create-full-refund", permissions=get_default_model_permissions(Order)), admin_url("^orders/(?P<pk>\d+)/$", "shuup.admin.modules.orders.views.OrderDetailView", name="order.detail", permissions=get_default_model_permissions(Order)), admin_url("^orders/new/$", "shuup.admin.modules.orders.views.OrderEditView", name="order.new", permissions=["shuup.add_order"]), admin_url("^orders/(?P<pk>\d+)/edit/$", "shuup.admin.modules.orders.views.OrderEditView", name="order.edit", permissions=["shuup.change_order"]), admin_url( "^orders/$", "shuup.admin.modules.orders.views.OrderListView", name="order.list", permissions=get_default_model_permissions(Order), ), admin_url( "^orders/list-settings/", "shuup.admin.modules.settings.views.ListSettingsView", name="order.list_settings", permissions=get_default_model_permissions(Order), ) ] def get_menu_entries(self, request): return [ MenuEntry(text=_("Orders"), icon="fa fa-inbox", url="shuup_admin:order.list", category=ORDERS_MENU_CATEGORY, ordering=1, aliases=[_("Show orders")]), ] def get_required_permissions(self): return (get_permissions_from_urls(self.get_urls()) | get_default_model_permissions(Contact) | get_default_model_permissions(Order) | get_default_model_permissions(Product)) def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: orders = Order.objects.filter( Q(identifier__istartswith=query) | Q(reference_number__istartswith=query) | Q(email__icontains=query) | Q(phone__icontains=query)).order_by("-id")[:15] for i, order in enumerate(orders): relevance = 100 - i yield SearchResult(text=six.text_type(order), url=get_model_url(order), category=_("Orders"), relevance=relevance) def get_notifications(self, request): old_open_orders = Order.objects.filter( status__role=OrderStatusRole.INITIAL, order_date__lt=now() - timedelta(days=4)).count() if old_open_orders: yield Notification(title=_("Outstanding Orders"), text=_("%d outstanding orders") % old_open_orders, kind="danger") def get_model_url(self, object, kind): return derive_model_url(Order, "shuup_admin:order", object, kind)
def get_menu_entries(self, request): return [ MenuEntry( text=self.name, icon="fa fa-image", url="shuup_admin:reports.list", category=REPORTS_MENU_CATEGORY ) ]
def get_breadcrumb_parents(self): return [ MenuEntry(text="%s" % self.object, url=get_model_url(self.object, shop=self.request.shop)) ]
class CategoryModule(AdminModule): name = _("Categories") category = _("Categories") breadcrumbs_menu_entry = MenuEntry(text=name, url="shuup_admin:category.list", category=PRODUCTS_MENU_CATEGORY) def get_urls(self): return [ admin_url( r"^categories/(?P<pk>\d+)/copy-visibility/$", "shuup.admin.modules.categories.views.CategoryCopyVisibilityView", name="category.copy_visibility", ), admin_url( r"^categories/(?P<pk>\d+)/delete/$", "shuup.admin.modules.categories.views.CategoryDeleteView", name="category.delete", ), ] + get_edit_and_list_urls( url_prefix="^categories", view_template="shuup.admin.modules.categories.views.Category%sView", name_template="category.%s", ) def get_menu_entries(self, request): return [ MenuEntry( text=_("Categories"), icon="fa fa-sitemap", url="shuup_admin:category.list", category=PRODUCTS_MENU_CATEGORY, ordering=2, ) ] def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: shop = get_shop(request) categories = (Category.objects.all_except_deleted( shop=shop).filter( Q(translations__name__icontains=query) | Q(identifier__icontains=query)).distinct().order_by( "tree_id", "lft")) for i, category in enumerate(categories[:10]): relevance = 100 - i yield SearchResult( text=six.text_type(category), url=get_model_url(category), category=self.category, relevance=relevance, ) def get_help_blocks(self, request, kind): from shuup.admin.utils.permissions import has_permission if has_permission(request.user, "category.new"): yield SimpleHelpBlock( text=_("Add a product category to organize your products."), actions=[{ "text": _("New category"), "url": get_model_url(Category, "new") }], icon_url="shuup_admin/img/category.png", category=HelpBlockCategory.PRODUCTS, priority=1, done=Category.objects.filter( shops=request.shop).exists() if kind == "setup" else False, ) def get_model_url(self, object, kind, shop=None): return derive_model_url(Category, "shuup_admin:category", object, kind) def get_extra_permissions(self) -> Iterable[str]: return [get_object_selector_permission_name(Category)] def get_permissions_help_texts(self) -> Iterable[str]: return { get_object_selector_permission_name(Category): _("Allow the user to select categories in admin.") }
class TaskAdminModule(AdminModule): name = _("Tasks") breadcrumbs_menu_entry = MenuEntry(name, "shuup_admin:task.list") def get_urls(self): return get_edit_and_list_urls( url_prefix="^tasks", view_template="shuup.tasks.admin_module.views.Task%sView", name_template="task.%s", permissions=get_default_model_permissions(Task) ) + [ admin_url( "^tasks/(?P<pk>\d+)/delete/$", "shuup.tasks.admin_module.views.TaskDeleteView", name="task.delete", permissions=get_default_model_permissions(Task) ), admin_url( "^tasks/(?P<pk>\d+)/set_status/$", "shuup.tasks.admin_module.views.TaskSetStatusView", name="task.set_status", permissions=get_default_model_permissions(Task) ), ] def get_menu_entries(self, request): return [ MenuEntry( text=_("Tasks"), icon="fa fa-file-text", url="shuup_admin:task.list", category=CONTACTS_MENU_CATEGORY, ordering=4, aliases=[_("Show Tasks")] ) ] def get_required_permissions(self): return get_default_model_permissions(Task) def get_model_url(self, object, kind, shop=None): return derive_model_url(Task, "shuup_admin:task", object, kind) def get_dashboard_blocks(self, request): """ Return the latest 10 pending tasks """ contact = get_person_contact(request.user) tasks = ( Task.objects.for_shop(get_shop(request)) .assigned_to(contact) .exclude(status__in=(TaskStatus.DELETED, TaskStatus.COMPLETED)) .order_by("-priority") )[:10] if tasks.exists(): tasks_block = DashboardContentBlock.by_rendering_template( "articles", request, "shuup/admin/tasks/tasks_dashboard_block.jinja", context=dict(tasks=tasks) ) tasks_block.size = "medium" yield tasks_block def get_search_results(self, request, query): shop = get_shop(request) if len(query) >= 3: contact = get_person_contact(request.user) tasks = ( Task.objects .for_shop(get_shop(request)) .filter(name__icontains=query) .assigned_to(contact) .exclude(status__in=(TaskStatus.DELETED, TaskStatus.COMPLETED)) ) for task in tasks: yield SearchResult( text=force_text("{task_name} [{task_status}]".format(**dict( task_name=task.name, task_status=task.status ))), url=get_model_url(task, shop=shop), category=_("Tasks") )
def infer(cls, context): """ Infer breadcrumbs from the rendering context. :param context: Jinja Context :type context: jinja2.runtime.Context :return: Breadcrumbs object or None if things fail :rtype: Breadcrumbs|None """ request = context["request"] if not getattr(request, "resolver_match", None): # If we don't have a resolver match, we can't infer anything. return None url_names = ( request.resolver_match.url_name, "%s:%s" % (request.resolver_match.app_name, request.resolver_match.url_name) ) url_admin_module = _get_admin_module_for_url(url_names) # Synthesize a menu entry for the current view. current_view_entry = MenuEntry(url=request.path, text="") if url_admin_module: # See if we have an idea for the title of this view from the menu entries for entry in url_admin_module.get_menu_entries(request): if entry.original_url in url_names: current_view_entry.text = entry.text break # See if we have a title for the synthesized entry in the context. view = context.get("view") # This should be the CBV view object. title = ( context.get("title") or context.get("breadcrumb_title") or (view and getattr(view, "title", None)) ) if title: current_view_entry.text = force_text(title) # Begin building the entries... entries = [] # See if we have the top level menu entry ("Contacts" for example). if url_admin_module and url_admin_module.breadcrumbs_menu_entry: # (But don't duplicate steps) if url_admin_module.breadcrumbs_menu_entry.url != request.path or not current_view_entry.text: entries.append(url_admin_module.breadcrumbs_menu_entry) # See if the view declares parents... parent_getter = getattr(view, "get_breadcrumb_parents", None) if parent_getter: entries.extend(parent_getter() or ()) # If the current entry seems valid (would be visible), then go for it! if current_view_entry.text: entries.append(current_view_entry) return cls(entries)
class ContactModule(AdminModule): name = _("Contacts") category = name breadcrumbs_menu_entry = MenuEntry(text=name, url="shuup_admin:contact.list") def get_urls(self): return [ admin_url( "^contacts/new/$", "shuup.admin.modules.contacts.views.ContactEditView", kwargs={"pk": None}, name="contact.new", permissions=["shuup.add_contact"], ), admin_url( "^contacts/(?P<pk>\d+)/edit/$", "shuup.admin.modules.contacts.views.ContactEditView", name="contact.edit", permissions=["shuup.change_contact"], ), admin_url( "^contacts/(?P<pk>\d+)/$", "shuup.admin.modules.contacts.views.ContactDetailView", name="contact.detail", permissions=get_default_model_permissions(Contact), ), admin_url( "^contacts/reset-password/(?P<pk>\d+)/$", "shuup.admin.modules.contacts.views.ContactResetPasswordView", name="contact.reset_password", permissions=get_default_model_permissions(Contact), ), admin_url( "^contacts/$", "shuup.admin.modules.contacts.views.ContactListView", name="contact.list", permissions=get_default_model_permissions(Contact), ), ] def get_menu_category_icons(self): return {self.category: "fa fa-users"} def get_menu_entries(self, request): return [ MenuEntry(text=_("Contacts"), icon="fa fa-users", url="shuup_admin:contact.list", category=self.category) ] def get_required_permissions(self): return get_permissions_from_urls( self.get_urls()) | get_default_model_permissions(Contact) def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: contacts = Contact.objects.filter( Q(name__icontains=query) | Q(email=query)) for i, contact in enumerate(contacts[:10]): relevance = 100 - i yield SearchResult(text=six.text_type(contact), url=get_model_url(contact), category=self.category, relevance=relevance) def get_dashboard_blocks(self, request): yield get_active_customers_block(request) def get_model_url(self, object, kind): return derive_model_url(Contact, "shuup_admin:contact", object, kind)
class OrderModule(AdminModule): name = _("Orders") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:order.list") def get_urls(self): return [ admin_url( "^orders/(?P<pk>\d+)/create-shipment/$", "shuup.admin.modules.orders.views.OrderCreateShipmentView", name="order.create-shipment", permissions=["shuup.add_shipment"]), admin_url("^shipments/(?P<pk>\d+)/delete/$", "shuup.admin.modules.orders.views.ShipmentDeleteView", name="order.delete-shipment", permissions=["shuup.delete_shipment"]), admin_url( "^orders/(?P<pk>\d+)/create-payment/$", "shuup.admin.modules.orders.views.OrderCreatePaymentView", name="order.create-payment", permissions=["shuup.add_payment"]), admin_url("^orders/(?P<pk>\d+)/set-paid/$", "shuup.admin.modules.orders.views.OrderSetPaidView", name="order.set-paid", permissions=["shuup.add_payment"]), admin_url("^orders/(?P<pk>\d+)/set-status/$", "shuup.admin.modules.orders.views.OrderSetStatusView", name="order.set-status", permissions=get_default_model_permissions(Order)), admin_url("^orders/(?P<pk>\d+)/new-log-entry/$", "shuup.admin.modules.orders.views.NewLogEntryView", name="order.new-log-entry", permissions=get_default_model_permissions(Order)), admin_url( "^orders/(?P<pk>\d+)/update-admin-comment/$", "shuup.admin.modules.orders.views.UpdateAdminCommentView", name="order.update-admin-comment", permissions=get_default_model_permissions(Order)), admin_url("^orders/(?P<pk>\d+)/create-refund/$", "shuup.admin.modules.orders.views.OrderCreateRefundView", name="order.create-refund", permissions=get_default_model_permissions(Order)), admin_url( "^orders/(?P<pk>\d+)/create-refund/full-refund$", "shuup.admin.modules.orders.views.OrderCreateFullRefundView", name="order.create-full-refund", permissions=get_default_model_permissions(Order)), admin_url("^orders/(?P<pk>\d+)/$", "shuup.admin.modules.orders.views.OrderDetailView", name="order.detail", permissions=get_default_model_permissions(Order)), admin_url("^orders/new/$", "shuup.admin.modules.orders.views.OrderEditView", name="order.new", permissions=["shuup.add_order"]), admin_url("^orders/(?P<pk>\d+)/edit/$", "shuup.admin.modules.orders.views.OrderEditView", name="order.edit", permissions=["shuup.change_order"]), admin_url( "^orders/$", "shuup.admin.modules.orders.views.OrderListView", name="order.list", permissions=get_default_model_permissions(Order), ), admin_url( "^orders/list-settings/", "shuup.admin.modules.settings.views.ListSettingsView", name="order.list_settings", permissions=get_default_model_permissions(Order), ), admin_url("^orders/(?P<pk>\d+)/edit-addresses/$", "shuup.admin.modules.orders.views.OrderAddressEditView", name="order.edit-addresses", permissions=["shuup.change_order"]), ] + get_edit_and_list_urls( url_prefix="^order-status", view_template="shuup.admin.modules.orders.views.OrderStatus%sView", name_template="order_status.%s", permissions=get_default_model_permissions(OrderStatus)) def get_menu_entries(self, request): return [ MenuEntry(text=_("Orders"), icon="fa fa-inbox", url="shuup_admin:order.list", category=ORDERS_MENU_CATEGORY, ordering=1, aliases=[_("Show orders")]), # MenuEntry( # text=_("Order Status"), # icon="fa fa-inbox", # url="shuup_admin:order_status.list", # category=STOREFRONT_MENU_CATEGORY, # subcategory="settings", # ordering=1, # aliases=[_("List Statuses")] # ), ] def get_required_permissions(self): return (get_permissions_from_urls(self.get_urls()) | get_default_model_permissions(Contact) | get_default_model_permissions(Order) | get_default_model_permissions(Product)) def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: orders = Order.objects.filter( Q(identifier__istartswith=query) | Q(reference_number__istartswith=query) | Q(email__icontains=query) | Q(phone__icontains=query)).order_by("-id")[:15] for i, order in enumerate(orders): relevance = 100 - i yield SearchResult(text=six.text_type(order), url=get_model_url(order), category=_("Orders"), relevance=relevance) def get_notifications(self, request): old_open_orders = Order.objects.filter( status__role=OrderStatusRole.INITIAL, order_date__lt=now() - timedelta(days=4)).count() if old_open_orders: yield Notification(title=_("Outstanding Orders"), text=_("%d outstanding orders") % old_open_orders, kind="danger") def get_model_url(self, object, kind): if hasattr(object, "role"): return derive_model_url(OrderStatus, "shuup_admin:order_status", object, kind) return derive_model_url(Order, "shuup_admin:order", object, kind) def get_help_blocks(self, request, kind): if kind == "quicklink": actions = [{ "text": _("New order"), "url": self.get_model_url(Order, "new") }] yield SimpleHelpBlock( text=_("New order"), actions=actions, icon_url="shuup_admin/img/product.png", priority=0, category=HelpBlockCategory.ORDERS, done=Order.objects.exists() if kind == "setup" else False)
class XthemeAdminModule(AdminModule): """ Admin module for Xtheme. Allows theme activation/deactivation and further configuration. """ name = _("Shuup Extensible Theme Engine") breadcrumbs_menu_entry = MenuEntry(_("Themes"), "shuup_admin:xtheme.config", category=CONTENT_MENU_CATEGORY) def get_urls(self): # doccov: ignore return [ admin_url( r"^xtheme/guide/(?P<theme_identifier>.+?)/", "shuup.xtheme.admin_module.views.ThemeGuideTemplateView", name="xtheme.guide", ), admin_url( r"^xtheme/configure/(?P<theme_identifier>.+?)/", "shuup.xtheme.admin_module.views.ThemeConfigDetailView", name="xtheme.config_detail", ), admin_url(r"^xtheme/theme", "shuup.xtheme.admin_module.views.ThemeConfigView", name="xtheme.config"), ] def get_menu_entries(self, request): # doccov: ignore return [ MenuEntry( text=_("Themes"), icon="fa fa-paint-brush", url="shuup_admin:xtheme.config", category=CONTENT_MENU_CATEGORY, ordering=1, ) ] def get_help_blocks(self, request, kind): theme = getattr(request, "theme", None) or get_current_theme(request.shop) if kind == "quicklink" and theme: yield SimpleHelpBlock( text=_("Customize the look and feel of your shop"), actions=[ { "text": _("Customize theme"), "url": reverse( "shuup_admin:xtheme.config_detail", kwargs={"theme_identifier": theme.identifier} ), } ], priority=200, category=HelpBlockCategory.STOREFRONT, icon_url="xtheme/theme.png", ) def get_notifications(self, request): try: engine = engines["jinja2"] except KeyError: engine = None if engine and isinstance(engine, Jinja2): # The engine is what we expect... if isinstance(engine.env, XthemeEnvironment): # ... and it's capable of loading themes... if not (getattr(request, "theme", None) or get_current_theme(request.shop)): # ... but there's no theme active?! # Panic! yield Notification( text=_("No theme is active. Click here to activate one."), title=_("Theming"), url="shuup_admin:xtheme.config", )
class ShopModule(AdminModule): name = _("Shops") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:shop.list") def get_urls(self): return [ admin_url("^shops/(?P<pk>\d+)/enable/$", "shuup.admin.modules.shops.views.ShopEnablerView", name="shop.enable", permissions=get_default_model_permissions(Shop)), admin_url("^shops/(?P<pk>\d+)/select/$", "shuup.admin.modules.shops.views.ShopSelectView", name="shop.select", permissions=get_default_model_permissions(Shop)), ] + get_edit_and_list_urls( url_prefix="^shops", view_template="shuup.admin.modules.shops.views.Shop%sView", name_template="shop.%s", permissions=get_default_model_permissions(Shop)) def get_menu_entries(self, request): return [ MenuEntry(text=self.name, icon="fa fa-house", url="shuup_admin:shop.list", category=STOREFRONT_MENU_CATEGORY, subcategory="settings", ordering=4), ] def get_help_blocks(self, request, kind): if kind == "setup": shop = request.shop yield SimpleHelpBlock( text=_("Add a logo to make your store stand out"), actions=[{ "text": _("Add logo"), "url": self.get_model_url(shop, "edit"), "hash": "#shop-images-section" }], icon_url="shuup_admin/img/logo_icon.svg", done=shop.logo, required=False) def get_required_permissions(self): return get_default_model_permissions( Shop) | get_default_model_permissions(File) def get_model_url(self, object, kind, shop=None): return derive_model_url(Shop, "shuup_admin:shop", object, kind) def get_search_results(self, request, query): if not settings.SHUUP_ENABLE_MULTIPLE_SHOPS: return minimum_query_length = 3 if len(query) >= minimum_query_length: shops = Shop.objects.get_for_user(request.user).filter( translations__name__icontains=query, status=ShopStatus.ENABLED) for i, shop in enumerate(shops[:10]): relevance = 100 - i yield SearchResult( text=(_('Set "{}" as the active shop')).format(shop.name), url=get_model_url(shop, "select"), category=( _("Available Shops [currently active: {}]")).format( request.shop.name), relevance=relevance)
class CategoryModule(AdminModule): name = _("Categories") category = _("Products") breadcrumbs_menu_entry = MenuEntry(text=name, url="shuup_admin:category.list", category=PRODUCTS_MENU_CATEGORY) def get_urls(self): return [ admin_url( "^categories/(?P<pk>\d+)/copy-visibility/$", "shuup.admin.modules.categories.views.CategoryCopyVisibilityView", name="category.copy_visibility", permissions=get_default_model_permissions(Category) ), admin_url( "^categories/(?P<pk>\d+)/delete/$", "shuup.admin.modules.categories.views.CategoryDeleteView", name="category.delete", permissions=get_default_model_permissions(Category) ), ] + get_edit_and_list_urls( url_prefix="^categories", view_template="shuup.admin.modules.categories.views.Category%sView", name_template="category.%s", permissions=get_default_model_permissions(Category), ) def get_menu_entries(self, request): return [ MenuEntry( text=_("Categories"), icon="fa fa-sitemap", url="shuup_admin:category.list", category=PRODUCTS_MENU_CATEGORY, ordering=2 ) ] def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: categories = Category.objects.filter( Q(translations__name__icontains=query) | Q(identifier__icontains=query) ).distinct().order_by("tree_id", "lft") for i, category in enumerate(categories[:10]): relevance = 100 - i yield SearchResult( text=six.text_type(category), url=get_model_url(category), category=self.category, relevance=relevance ) def get_help_blocks(self, request, kind): yield SimpleHelpBlock( text=_("Add a product category to organize your products"), actions=[{ "text": _("Add a category"), "url": get_model_url(Category, "new") }], icon_url="shuup_admin/img/category.png", priority=0, done=Category.objects.exists() if kind == "setup" else False ) def get_required_permissions(self): return get_default_model_permissions(Category) | get_default_model_permissions(File) def get_model_url(self, object, kind): return derive_model_url(Category, "shuup_admin:category", object, kind)
def get_breadcrumb_parents(self): return [MenuEntry(text=self.parent_name, url=self.parent_url)]
def get_breadcrumb_parents(self): return [ MenuEntry(text=force_text( self.model._meta.verbose_name_plural).title(), url="shuup_admin:service_provider.list") ]
def infer(cls, context): """ Infer breadcrumbs from the rendering context. :param context: Jinja Context :type context: jinja2.runtime.Context :return: Breadcrumbs object or None if things fail :rtype: Breadcrumbs|None """ request = context["request"] if not getattr(request, "resolver_match", None): # If we don't have a resolver match, we can't infer anything. return None url_names = ( request.resolver_match.url_name, "%s:%s" % (request.resolver_match.app_name, request.resolver_match.url_name) ) url_admin_module = _get_admin_module_for_url(url_names) # Synthesize a menu entry for the current view. current_view_entry = MenuEntry(url=request.path, text="") if url_admin_module: # See if we have an idea for the title of this view from the menu entries for entry in url_admin_module.get_menu_entries(request): if entry.original_url in url_names: current_view_entry.text = entry.text break # See if we have a title for the synthesized entry in the context. view = context.get("view") # This should be the CBV view object. title = ( context.get("title") or context.get("breadcrumb_title") or (view and getattr(view, "title", None)) ) if title: current_view_entry.text = force_text(title) # Begin building the entries... entries = [] # See if we have the top level menu entry ("Contacts" for example). if url_admin_module and url_admin_module.breadcrumbs_menu_entry: # (But don't duplicate steps) if url_admin_module.breadcrumbs_menu_entry.url != request.path or not current_view_entry.text: entries.append(url_admin_module.breadcrumbs_menu_entry) # See if the view declares parents... parent_getter = getattr(view, "get_breadcrumb_parents", None) if parent_getter: entries.extend(parent_getter() or ()) # If the current entry seems valid (would be visible), then go for it! if current_view_entry.text: entries.append(current_view_entry) return cls(entries)
class ProductModule(AdminModule): name = _("Products") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:shop_product.list") def get_urls(self): return [ admin_url(r"^products/(?P<pk>\d+)/delete/$", "shuup.admin.modules.products.views.ProductDeleteView", name="shop_product.delete"), admin_url( r"^products/(?P<pk>\d+)/media/$", "shuup.admin.modules.products.views.ProductMediaEditView", name="shop_product.edit_media"), admin_url( r"^products/(?P<pk>\d+)/media/add/$", "shuup.admin.modules.products.views.ProductMediaBulkAdderView", name="shop_product.add_media"), admin_url( r"^products/(?P<pk>\d+)/crosssell/$", "shuup.admin.modules.products.views.ProductCrossSellEditView", name="shop_product.edit_cross_sell"), admin_url( r"^products/(?P<pk>\d+)/variation/$", "shuup.admin.modules.products.views.ProductVariationView", name="shop_product.edit_variation"), admin_url(r"^products/(?P<pk>\d+)/package/$", "shuup.admin.modules.products.views.ProductPackageView", name="shop_product.edit_package"), admin_url(r"^products/mass-edit/$", "shuup.admin.modules.products.views.ProductMassEditView", name="shop_product.mass_edit"), admin_url( r"^products/(?P<pk>\d+)/copy/$", "shuup.admin.modules.products.views.copy.ProductCopyView", name="shop_product.copy"), ] + get_edit_and_list_urls( url_prefix="^products", view_template="shuup.admin.modules.products.views.Product%sView", name_template="shop_product.%s") def get_menu_entries(self, request): return [ MenuEntry(text=_("Products"), icon="fa fa-cube", url="shuup_admin:shop_product.list", category=PRODUCTS_MENU_CATEGORY, ordering=1) ] def get_search_results(self, request, query): shop = request.shop minimum_query_length = 3 skus_seen = set() if len(query) >= minimum_query_length: pk_counter = Counter() pk_counter.update( Product.objects.filter(sku__startswith=query).values_list( "pk", flat=True)) name_q = Q() for part in split_query(query, minimum_query_length): name_q &= Q(name__icontains=part) pk_counter.update( Product._parler_meta.root_model.objects.filter( name_q).values_list("master_id", flat=True)) pks = [pk for (pk, count) in pk_counter.most_common(10)] for product in Product.objects.filter( pk__in=pks, shop_products__shop_id=shop.id): relevance = 100 - pk_counter.get(product.pk, 0) skus_seen.add(product.sku.lower()) yield SearchResult(text=force_text(product), url=get_model_url(product, shop=request.shop), category=_("Products"), relevance=relevance) if len(query) >= minimum_query_length: url = reverse("shuup_admin:shop_product.new") if " " in query: yield SearchResult( text=_("Create Product Called \"%s\"") % query, url=manipulate_query_string(url, name=query), is_action=True) else: if query.lower() not in skus_seen: yield SearchResult( text=_("Create Product with SKU \"%s\"") % query, url=manipulate_query_string(url, sku=query), is_action=True) def get_help_blocks(self, request, kind): actions = [] from shuup.admin.utils.permissions import has_permission if has_permission(request.user, "shop_product.new"): actions.append({ "text": _("New product"), "url": self.get_model_url(ShopProduct, "new") }) if "shuup.importer" in settings.INSTALLED_APPS and has_permission( request.user, "importer.import"): actions.append({ "text": _("Import"), "url": reverse("shuup_admin:importer.import") }) if actions: yield SimpleHelpBlock( text=_("Add a product to see it in your store"), actions=actions, icon_url="shuup_admin/img/product.png", priority=0, category=HelpBlockCategory.PRODUCTS, done=Product.objects.filter( shop_products__shop=request.shop).exists() if kind == "setup" else False) def get_model_url(self, object, kind, shop=None): if isinstance(object, Product): if not shop: try: shop = object.shop_products.first().shop except ObjectDoesNotExist: return None object = object.get_shop_instance(shop) return derive_model_url(ShopProduct, "shuup_admin:shop_product", object, kind)
def get_menu_entries(self, request): return [ MenuEntry(text=self.name, url=self.menu_entry_url, category=self.category) ]
class ProductModule(AdminModule): name = _("Products") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:product.list") def get_urls(self): return [ admin_url("^products/(?P<pk>\d+)/delete/$", "shuup.admin.modules.products.views.ProductDeleteView", name="product.delete", permissions=["shuup.delete_product"]), admin_url( "^products/(?P<pk>\d+)/media/$", "shuup.admin.modules.products.views.ProductMediaEditView", name="product.edit_media", permissions=get_default_model_permissions(Product), ), admin_url( "^products/(?P<pk>\d+)/crosssell/$", "shuup.admin.modules.products.views.ProductCrossSellEditView", name="product.edit_cross_sell", permissions=get_default_model_permissions(ProductCrossSell), ), admin_url( "^products/(?P<pk>\d+)/variation/$", "shuup.admin.modules.products.views.ProductVariationView", name="product.edit_variation", permissions=get_default_model_permissions( ProductVariationResult), ), admin_url( "^products/(?P<pk>\d+)/package/$", "shuup.admin.modules.products.views.ProductPackageView", name="product.edit_package", permissions=get_default_model_permissions(ProductPackageLink)), admin_url("^products/mass-edit/$", "shuup.admin.modules.products.views.ProductMassEditView", name="product.mass_edit", permissions=get_default_model_permissions(Product)) ] + get_edit_and_list_urls( url_prefix="^products", view_template="shuup.admin.modules.products.views.Product%sView", name_template="product.%s", permissions=get_default_model_permissions(Product)) def get_menu_entries(self, request): return [ MenuEntry(text=_("Products"), icon="fa fa-cube", url="shuup_admin:product.list", category=PRODUCTS_MENU_CATEGORY, ordering=1) ] def get_search_results(self, request, query): minimum_query_length = 3 skus_seen = set() if len(query) >= minimum_query_length: pk_counter = Counter() pk_counter.update( Product.objects.filter(sku__startswith=query).values_list( "pk", flat=True)) name_q = Q() for part in split_query(query, minimum_query_length): name_q &= Q(name__icontains=part) pk_counter.update( Product._parler_meta.root_model.objects.filter( name_q).values_list("master_id", flat=True)) pks = [pk for (pk, count) in pk_counter.most_common(10)] for product in Product.objects.filter(pk__in=pks): relevance = 100 - pk_counter.get(product.pk, 0) skus_seen.add(product.sku.lower()) yield SearchResult(text=force_text(product), url=get_model_url(product), category=_("Products"), relevance=relevance) if len(query) >= minimum_query_length: url = reverse("shuup_admin:product.new") if " " in query: yield SearchResult( text=_("Create Product Called \"%s\"") % query, url=manipulate_query_string(url, name=query), is_action=True) else: if query.lower() not in skus_seen: yield SearchResult( text=_("Create Product with SKU \"%s\"") % query, url=manipulate_query_string(url, sku=query), is_action=True) def get_help_blocks(self, request, kind): actions = [{ "text": _("Add a product"), "url": self.get_model_url(Product, "new") }] if "shuup.importer" in settings.INSTALLED_APPS: actions.append({ "text": _("Import products"), "url": reverse("shuup_admin:importer.import") }) yield SimpleHelpBlock( text=_("Add a product to see it in your store"), actions=actions, icon_url="shuup_admin/img/product.png", done=Product.objects.exists() if kind == "setup" else False) def get_required_permissions(self): return (get_permissions_from_urls(self.get_urls()) | get_default_model_permissions(Product) | get_default_model_permissions(File)) def get_model_url(self, object, kind): return derive_model_url(Product, "shuup_admin:product", object, kind)
def get_breadcrumb_parents(self): return [ MenuEntry(text="%s" % self.object, url=get_model_url(self.object)) ]
class UserModule(AdminModule): name = _("Users") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:user.list") def get_urls(self): permissions = get_default_model_permissions(get_user_model()) return [ admin_url("^users/(?P<pk>\d+)/change-password/$", "shuup.admin.modules.users.views.UserChangePasswordView", name="user.change-password", permissions=permissions), admin_url("^users/(?P<pk>\d+)/reset-password/$", "shuup.admin.modules.users.views.UserResetPasswordView", name="user.reset-password", permissions=permissions), admin_url( "^users/(?P<pk>\d+)/change-permissions/$", "shuup.admin.modules.users.views.UserChangePermissionsView", name="user.change-permissions", permissions=["auth.change_permission"]), admin_url("^users/(?P<pk>\d+)/$", "shuup.admin.modules.users.views.UserDetailView", name="user.detail", permissions=permissions), admin_url("^users/new/$", "shuup.admin.modules.users.views.UserDetailView", kwargs={"pk": None}, name="user.new", permissions=permissions), admin_url("^users/$", "shuup.admin.modules.users.views.UserListView", name="user.list", permissions=permissions), admin_url("^users/(?P<pk>\d+)/login/$", "shuup.admin.modules.users.views.LoginAsUserView", name="user.login-as", permissions=permissions), admin_url( "^contacts/list-settings/", "shuup.admin.modules.settings.views.ListSettingsView", name="user.list_settings", permissions=permissions, ) ] def get_menu_entries(self, request): return [ MenuEntry(text=_("Users"), icon="fa fa-users", url="shuup_admin:user.list", category=SETTINGS_MENU_CATEGORY, ordering=1) ] def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: users = get_user_model().objects.filter( Q(username__icontains=query) | Q(email=query)) for i, user in enumerate(users[:10]): relevance = 100 - i yield SearchResult(text=six.text_type(user), url=get_model_url(user), category=_("Contacts"), relevance=relevance) def get_required_permissions(self): return get_default_model_permissions(get_user_model()) def get_model_url(self, object, kind): return derive_model_url(get_user_model(), "shuup_admin:user", object, kind)
class UserModule(AdminModule): name = _("Users") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:user.list") def get_urls(self): permissions = get_default_model_permissions(get_user_model()) return [ admin_url("^users/(?P<pk>\d+)/change-password/$", "shuup.admin.modules.users.views.UserChangePasswordView", name="user.change-password", permissions=permissions), admin_url("^users/(?P<pk>\d+)/reset-password/$", "shuup.admin.modules.users.views.UserResetPasswordView", name="user.reset-password", permissions=permissions), admin_url( "^users/(?P<pk>\d+)/change-permissions/$", "shuup.admin.modules.users.views.UserChangePermissionsView", name="user.change-permissions", permissions=["auth.change_permission"]), admin_url("^users/(?P<pk>\d+)/$", "shuup.admin.modules.users.views.UserDetailView", name="user.detail", permissions=permissions), admin_url("^users/new/$", "shuup.admin.modules.users.views.UserDetailView", kwargs={"pk": None}, name="user.new", permissions=permissions), admin_url("^users/$", "shuup.admin.modules.users.views.UserListView", name="user.list", permissions=permissions), admin_url("^users/(?P<pk>\d+)/login/$", "shuup.admin.modules.users.views.LoginAsUserView", name="user.login-as", permissions=permissions), admin_url( "^contacts/list-settings/", "shuup.admin.modules.settings.views.ListSettingsView", name="user.list_settings", permissions=permissions, ) ] def get_menu_entries(self, request): return [ MenuEntry(text=_("Users"), icon="fa fa-users", url="shuup_admin:user.list", category=SETTINGS_MENU_CATEGORY, subcategory="permissions", ordering=1) ] def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: users = get_user_model().objects.filter( Q(username__icontains=query) | Q(email=query)) for i, user in enumerate(users[:10]): relevance = 100 - i yield SearchResult(text=six.text_type(user), url=get_model_url(user), category=_("Contacts"), relevance=relevance) def get_help_blocks(self, request, kind): yield SimpleHelpBlock( text=_("Add some users to help manage your shop"), actions=[{ "text": _("New user"), "url": self.get_model_url(get_user_model(), "new") }], priority=3, category=HelpBlockCategory.CONTACTS, icon_url="shuup_admin/img/users.png", done=get_user_model().objects.exists() if kind == "setup" else False) def get_required_permissions(self): return get_default_model_permissions(get_user_model()) def get_model_url(self, object, kind): return derive_model_url(get_user_model(), "shuup_admin:user", object, kind)
class OrderModule(CurrencyBound, AdminModule): name = _("Orders") breadcrumbs_menu_entry = MenuEntry(name, url="shuup_admin:order.list") def get_urls(self): return [ admin_url( "^orders/(?P<pk>\d+)/create-shipment/$", "shuup.admin.modules.orders.views.OrderCreateShipmentView", name="order.create-shipment", permissions=["shuup.add_shipment"]), admin_url( "^orders/(?P<pk>\d+)/create-payment/$", "shuup.admin.modules.orders.views.OrderCreatePaymentView", name="order.create-payment", permissions=["shuup.add_payment"]), admin_url("^orders/(?P<pk>\d+)/set-status/$", "shuup.admin.modules.orders.views.OrderSetStatusView", name="order.set-status", permissions=get_default_model_permissions(Order)), admin_url("^orders/(?P<pk>\d+)/new-log-entry/$", "shuup.admin.modules.orders.views.NewLogEntryView", name="order.new-log-entry", permissions=get_default_model_permissions(Order)), admin_url("^orders/(?P<pk>\d+)/create-refund/$", "shuup.admin.modules.orders.views.OrderCreateRefundView", name="order.create-refund", permissions=get_default_model_permissions(Order)), admin_url( "^orders/(?P<pk>\d+)/create-refund/full-refund$", "shuup.admin.modules.orders.views.OrderCreateFullRefundView", name="order.create-full-refund", permissions=get_default_model_permissions(Order)), admin_url("^orders/(?P<pk>\d+)/$", "shuup.admin.modules.orders.views.OrderDetailView", name="order.detail", permissions=get_default_model_permissions(Order)), admin_url("^orders/new/$", "shuup.admin.modules.orders.views.OrderEditView", name="order.new", permissions=["shuup.add_order"]), admin_url("^orders/(?P<pk>\d+)/edit/$", "shuup.admin.modules.orders.views.OrderEditView", name="order.edit", permissions=["shuup.change_order"]), admin_url( "^orders/$", "shuup.admin.modules.orders.views.OrderListView", name="order.list", permissions=get_default_model_permissions(Order), ), ] def get_menu_category_icons(self): return {self.name: "fa fa-inbox"} def get_menu_entries(self, request): category = _("Orders") return [ MenuEntry(text=_("Orders"), icon="fa fa-inbox", url="shuup_admin:order.list", category=category, aliases=[_("Show orders")]), ] def get_required_permissions(self): return (get_permissions_from_urls(self.get_urls()) | get_default_model_permissions(Contact) | get_default_model_permissions(Order) | get_default_model_permissions(Product)) def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: orders = Order.objects.filter( Q(identifier__istartswith=query) | Q(reference_number__istartswith=query) | Q(email__icontains=query) | Q(phone__icontains=query)).order_by("-id")[:15] for i, order in enumerate(orders): relevance = 100 - i yield SearchResult(text=six.text_type(order), url=get_model_url(order), category=_("Orders"), relevance=relevance) def get_dashboard_blocks(self, request): import shuup.admin.modules.orders.dashboard as dashboard currency = self.currency if not currency: return yield dashboard.get_sales_of_the_day_block(request, currency) yield dashboard.get_lifetime_sales_block(request, currency) yield dashboard.get_avg_purchase_size_block(request, currency) yield dashboard.get_open_orders_block(request, currency) yield dashboard.get_order_value_chart_dashboard_block( request, currency) def get_notifications(self, request): old_open_orders = Order.objects.filter( status__role=OrderStatusRole.INITIAL, order_date__lt=now() - timedelta(days=4)).count() if old_open_orders: yield Notification(title=_("Outstanding Orders"), text=_("%d outstanding orders") % old_open_orders, kind="danger") def get_model_url(self, object, kind): return derive_model_url(Order, "shuup_admin:order", object, kind)
class ContactModule(AdminModule): name = _("Contacts") breadcrumbs_menu_entry = MenuEntry(text=name, url="shuup_admin:contact.list", category=CONTACTS_MENU_CATEGORY) def get_urls(self): return [ admin_url( r"^contacts/new/$", "shuup.admin.modules.contacts.views.ContactEditView", kwargs={"pk": None}, name="contact.new", ), admin_url( r"^contacts/(?P<pk>\d+)/edit/$", "shuup.admin.modules.contacts.views.ContactEditView", name="contact.edit", ), admin_url( r"^contacts/(?P<pk>\d+)/$", "shuup.admin.modules.contacts.views.ContactDetailView", name="contact.detail", ), admin_url( r"^contacts/reset-password/(?P<pk>\d+)/$", "shuup.admin.modules.contacts.views.ContactResetPasswordView", name="contact.reset_password", ), admin_url(r"^contacts/$", "shuup.admin.modules.contacts.views.ContactListView", name="contact.list"), admin_url( r"^contacts/list-settings/", "shuup.admin.modules.settings.views.ListSettingsView", name="contact.list_settings", ), admin_url( r"^contacts/mass-edit/$", "shuup.admin.modules.contacts.views.ContactMassEditView", name="contact.mass_edit", ), admin_url( r"^contacts/mass-edit-group/$", "shuup.admin.modules.contacts.views.ContactGroupMassEditView", name="contact.mass_edit_group", ), ] def get_menu_entries(self, request): return [ MenuEntry( text=_("Contacts"), icon="fa fa-users", url="shuup_admin:contact.list", category=CONTACTS_MENU_CATEGORY, ordering=1, ) ] def get_search_results(self, request, query): minimum_query_length = 3 if len(query) >= minimum_query_length: filters = Q(Q(name__icontains=query) | Q(email=query)) # show only contacts which the shop has access if settings.SHUUP_ENABLE_MULTIPLE_SHOPS and settings.SHUUP_MANAGE_CONTACTS_PER_SHOP: filters &= Q(shops=request.shop) if not request.user.is_superuser: filters &= ~Q(PersonContact___user__is_superuser=True) contacts = Contact.objects.filter(filters) for i, contact in enumerate(contacts[:10]): relevance = 100 - i yield SearchResult( text=six.text_type(contact), url=get_model_url(contact), category=_("Contacts"), relevance=relevance ) def get_model_url(self, object, kind, shop=None): return derive_model_url(Contact, "shuup_admin:contact", object, kind)
class NotifyAdminModule(AdminModule): name = _(u"Notifications") breadcrumbs_menu_entry = MenuEntry(name, "shuup_admin:notify.script.list") def get_urls(self): permissions = get_default_model_permissions(NotificationModel) return [ admin_url("notify/script-item-editor/", "shuup.notify.admin_module.views.script_item_editor", name="notify.script-item-editor", permissions=permissions), admin_url("notify/script/content/(?P<pk>\d+)/", "shuup.notify.admin_module.views.EditScriptContentView", name="notify.script.edit-content", permissions=permissions), admin_url("notify/mark-read/(?P<pk>\d+)/$", self.mark_notification_read_view, name="notify.mark-read", permissions=permissions), ] + get_edit_and_list_urls( url_prefix="^notify/script", view_template="shuup.notify.admin_module.views.Script%sView", name_template="notify.script.%s", permissions=permissions) def get_menu_entries(self, request): return [ MenuEntry(text=_("Notifications"), icon="fa fa-code", url="shuup_admin:notify.script.list", category=SETTINGS_MENU_CATEGORY, ordering=9, aliases=[_("Show notification scripts")]) ] def get_required_permissions(self): return get_default_model_permissions(NotificationModel) @csrf_exempt def mark_notification_read_view(self, request, pk): if request.method == "POST": try: notif = NotificationModel.objects.for_user( request.user).get(pk=pk) except ObjectDoesNotExist: return JsonResponse({"error": "no such notification"}) notif.mark_read(request.user) return JsonResponse({"ok": True}) return JsonResponse({"error": "POST only"}) def get_notifications(self, request): notif_qs = NotificationModel.objects.unread_for_user( request.user).order_by("-id")[:15] for notif in notif_qs: if notif.priority == Priority.HIGH: kind = "warning" elif notif.priority == Priority.CRITICAL: kind = "danger" else: kind = "info" yield Notification(text=notif.message, url=notif.url, kind=kind, dismissal_url=reverse( "shuup_admin:notify.mark-read", kwargs={"pk": notif.pk}), datetime=notif.created_on) def get_model_url(self, object, kind): return derive_model_url(Script, "shuup_admin:notify.script", object, kind)
def get_menu_entries(self, request): return [ MenuEntry( text=_("Addons"), icon="fa fa-puzzle-piece", url="shuup_admin:addon.list", category=ADDONS_MENU_CATEGORY ) ]