├── .gitattributes ├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── MANIFEST.in ├── README.rst ├── docs ├── Makefile ├── locales │ └── en_US │ │ └── LC_MESSAGES │ │ ├── customization.po │ │ ├── errors.po │ │ ├── index.po │ │ └── quickstart.po ├── make.bat ├── requirements.txt └── source │ ├── _static │ ├── css │ │ └── custom.css │ └── fonts │ │ ├── css │ │ └── sahel.css │ │ └── sahel │ │ ├── Sahel-Black-FD.eot │ │ ├── Sahel-Black-FD.ttf │ │ ├── Sahel-Black-FD.woff │ │ ├── Sahel-Black-FD.woff2 │ │ ├── Sahel-Bold-FD.eot │ │ ├── Sahel-Bold-FD.ttf │ │ ├── Sahel-Bold-FD.woff │ │ ├── Sahel-Bold-FD.woff2 │ │ ├── Sahel-FD.eot │ │ ├── Sahel-FD.ttf │ │ ├── Sahel-FD.woff │ │ ├── Sahel-FD.woff2 │ │ ├── Sahel-Light-FD.eot │ │ ├── Sahel-Light-FD.ttf │ │ ├── Sahel-Light-FD.woff │ │ ├── Sahel-Light-FD.woff2 │ │ ├── Sahel-SemiBold-FD.eot │ │ ├── Sahel-SemiBold-FD.ttf │ │ ├── Sahel-SemiBold-FD.woff │ │ └── Sahel-SemiBold-FD.woff2 │ ├── conf.py │ ├── customization.rst │ ├── error.png │ ├── errors.rst │ ├── index.rst │ └── quickstart.rst ├── pwa ├── __init__.py ├── admin.py ├── apps.py ├── defaults.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── static │ └── pwa │ │ └── images │ │ ├── dino.gif │ │ └── icons │ │ ├── 128x128.png │ │ ├── 144x144.png │ │ ├── 152x152.png │ │ ├── 192x192.png │ │ ├── 384x384.png │ │ ├── 512x512.png │ │ ├── 72x72.png │ │ ├── 96x96.png │ │ └── favicon.ico ├── templates │ └── pwa │ │ ├── meta_script.html │ │ ├── metadata.html │ │ └── pwa_offline.html ├── templatetags │ ├── __init__.py │ └── pwa.py ├── urls.py └── views.py └── setup.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/python,venv,virtualenv,django,vs,visualstudio,visualstudiocode,pycharm+all,phpstorm+all,vim,database,git,gis,gitbook,direnv,dotenv,jenv,jetbrains+all 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=python,venv,virtualenv,django,vs,visualstudio,visualstudiocode,pycharm+all,phpstorm+all,vim,database,git,gis,gitbook,direnv,dotenv,jenv,jetbrains+all 4 | 5 | 6 | ### Custom ### 7 | env/ 8 | core/ 9 | manage.py 10 | 11 | ### Database ### 12 | *.accdb 13 | *.db 14 | *.dbf 15 | *.mdb 16 | *.pdb 17 | *.sqlite3 18 | 19 | ### direnv ### 20 | .direnv 21 | .envrc 22 | 23 | ### Django ### 24 | *.log 25 | *.pot 26 | *.pyc 27 | __pycache__/ 28 | local_settings.py 29 | db.sqlite3 30 | db.sqlite3-journal 31 | media 32 | 33 | # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ 34 | # in your Git repository. Update and uncomment the following line accordingly. 35 | # /staticfiles/ 36 | 37 | ### Django.Python Stack ### 38 | # Byte-compiled / optimized / DLL files 39 | *.py[cod] 40 | *$py.class 41 | 42 | # C extensions 43 | *.so 44 | 45 | # Distribution / packaging 46 | .Python 47 | build/ 48 | develop-eggs/ 49 | dist/ 50 | downloads/ 51 | eggs/ 52 | .eggs/ 53 | lib/ 54 | lib64/ 55 | parts/ 56 | sdist/ 57 | var/ 58 | wheels/ 59 | share/python-wheels/ 60 | *.egg-info/ 61 | .installed.cfg 62 | *.egg 63 | MANIFEST 64 | 65 | # PyInstaller 66 | # Usually these files are written by a python script from a template 67 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 68 | *.manifest 69 | *.spec 70 | 71 | # Installer logs 72 | pip-log.txt 73 | pip-delete-this-directory.txt 74 | 75 | # Unit test / coverage reports 76 | htmlcov/ 77 | .tox/ 78 | .nox/ 79 | .coverage 80 | .coverage.* 81 | .cache 82 | nosetests.xml 83 | coverage.xml 84 | *.cover 85 | *.py,cover 86 | .hypothesis/ 87 | .pytest_cache/ 88 | cover/ 89 | 90 | # Translations 91 | *.mo 92 | 93 | # Django stuff: 94 | 95 | # Flask stuff: 96 | instance/ 97 | .webassets-cache 98 | 99 | # Scrapy stuff: 100 | .scrapy 101 | 102 | # Sphinx documentation 103 | docs/_build/ 104 | 105 | # PyBuilder 106 | .pybuilder/ 107 | target/ 108 | 109 | # Jupyter Notebook 110 | .ipynb_checkpoints 111 | 112 | # IPython 113 | profile_default/ 114 | ipython_config.py 115 | 116 | # pyenv 117 | # For a library or package, you might want to ignore these files since the code is 118 | # intended to run in multiple environments; otherwise, check them in: 119 | # .python-version 120 | 121 | # pipenv 122 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 123 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 124 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 125 | # install all needed dependencies. 126 | #Pipfile.lock 127 | 128 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 129 | __pypackages__/ 130 | 131 | # Celery stuff 132 | celerybeat-schedule 133 | celerybeat.pid 134 | 135 | # SageMath parsed files 136 | *.sage.py 137 | 138 | # Environments 139 | .env 140 | .venv 141 | env/ 142 | venv/ 143 | ENV/ 144 | env.bak/ 145 | venv.bak/ 146 | 147 | # Spyder project settings 148 | .spyderproject 149 | .spyproject 150 | 151 | # Rope project settings 152 | .ropeproject 153 | 154 | # mkdocs documentation 155 | /site 156 | 157 | # mypy 158 | .mypy_cache/ 159 | .dmypy.json 160 | dmypy.json 161 | 162 | # Pyre type checker 163 | .pyre/ 164 | 165 | # pytype static type analyzer 166 | .pytype/ 167 | 168 | # Cython debug symbols 169 | cython_debug/ 170 | 171 | ### dotenv ### 172 | 173 | ### GIS ### 174 | *.gpx 175 | *.kml 176 | 177 | ### Git ### 178 | # Created by git for backups. To disable backups in Git: 179 | # $ git config --global mergetool.keepBackup false 180 | *.orig 181 | 182 | # Created by git when using merge tools for conflicts 183 | *.BACKUP.* 184 | *.BASE.* 185 | *.LOCAL.* 186 | *.REMOTE.* 187 | *_BACKUP_*.txt 188 | *_BASE_*.txt 189 | *_LOCAL_*.txt 190 | *_REMOTE_*.txt 191 | 192 | ### GitBook ### 193 | # Node rules: 194 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 195 | .grunt 196 | 197 | ## Dependency directory 198 | ## Commenting this out is preferred by some people, see 199 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 200 | node_modules 201 | 202 | # Book build output 203 | _book 204 | 205 | # eBook build output 206 | *.epub 207 | *.mobi 208 | *.pdf 209 | 210 | ### JEnv ### 211 | # JEnv local Java version configuration file 212 | .java-version 213 | 214 | # Used by previous versions of JEnv 215 | .jenv-version 216 | 217 | ### JetBrains+all ### 218 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 219 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 220 | 221 | # User-specific stuff 222 | .idea/**/workspace.xml 223 | .idea/**/tasks.xml 224 | .idea/**/usage.statistics.xml 225 | .idea/**/dictionaries 226 | .idea/**/shelf 227 | 228 | # AWS User-specific 229 | .idea/**/aws.xml 230 | 231 | # Generated files 232 | .idea/**/contentModel.xml 233 | 234 | # Sensitive or high-churn files 235 | .idea/**/dataSources/ 236 | .idea/**/dataSources.ids 237 | .idea/**/dataSources.local.xml 238 | .idea/**/sqlDataSources.xml 239 | .idea/**/dynamic.xml 240 | .idea/**/uiDesigner.xml 241 | .idea/**/dbnavigator.xml 242 | 243 | # Gradle 244 | .idea/**/gradle.xml 245 | .idea/**/libraries 246 | 247 | # Gradle and Maven with auto-import 248 | # When using Gradle or Maven with auto-import, you should exclude module files, 249 | # since they will be recreated, and may cause churn. Uncomment if using 250 | # auto-import. 251 | # .idea/artifacts 252 | # .idea/compiler.xml 253 | # .idea/jarRepositories.xml 254 | # .idea/modules.xml 255 | # .idea/*.iml 256 | # .idea/modules 257 | # *.iml 258 | # *.ipr 259 | 260 | # CMake 261 | cmake-build-*/ 262 | 263 | # Mongo Explorer plugin 264 | .idea/**/mongoSettings.xml 265 | 266 | # File-based project format 267 | *.iws 268 | 269 | # IntelliJ 270 | out/ 271 | 272 | # mpeltonen/sbt-idea plugin 273 | .idea_modules/ 274 | 275 | # JIRA plugin 276 | atlassian-ide-plugin.xml 277 | 278 | # Cursive Clojure plugin 279 | .idea/replstate.xml 280 | 281 | # Crashlytics plugin (for Android Studio and IntelliJ) 282 | com_crashlytics_export_strings.xml 283 | crashlytics.properties 284 | crashlytics-build.properties 285 | fabric.properties 286 | 287 | # Editor-based Rest Client 288 | .idea/httpRequests 289 | 290 | # Android studio 3.1+ serialized cache file 291 | .idea/caches/build_file_checksums.ser 292 | 293 | ### JetBrains+all Patch ### 294 | # Ignores the whole .idea folder and all .iml files 295 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 296 | 297 | .idea/ 298 | 299 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 300 | 301 | *.iml 302 | modules.xml 303 | .idea/misc.xml 304 | *.ipr 305 | 306 | # Sonarlint plugin 307 | .idea/sonarlint 308 | 309 | ### PhpStorm+all ### 310 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 311 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 312 | 313 | # User-specific stuff 314 | 315 | # AWS User-specific 316 | 317 | # Generated files 318 | 319 | # Sensitive or high-churn files 320 | 321 | # Gradle 322 | 323 | # Gradle and Maven with auto-import 324 | # When using Gradle or Maven with auto-import, you should exclude module files, 325 | # since they will be recreated, and may cause churn. Uncomment if using 326 | # auto-import. 327 | # .idea/artifacts 328 | # .idea/compiler.xml 329 | # .idea/jarRepositories.xml 330 | # .idea/modules.xml 331 | # .idea/*.iml 332 | # .idea/modules 333 | # *.iml 334 | # *.ipr 335 | 336 | # CMake 337 | 338 | # Mongo Explorer plugin 339 | 340 | # File-based project format 341 | 342 | # IntelliJ 343 | 344 | # mpeltonen/sbt-idea plugin 345 | 346 | # JIRA plugin 347 | 348 | # Cursive Clojure plugin 349 | 350 | # Crashlytics plugin (for Android Studio and IntelliJ) 351 | 352 | # Editor-based Rest Client 353 | 354 | # Android studio 3.1+ serialized cache file 355 | 356 | ### PhpStorm+all Patch ### 357 | # Ignores the whole .idea folder and all .iml files 358 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 359 | 360 | 361 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 362 | 363 | 364 | # Sonarlint plugin 365 | 366 | ### PyCharm+all ### 367 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 368 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 369 | 370 | # User-specific stuff 371 | 372 | # AWS User-specific 373 | 374 | # Generated files 375 | 376 | # Sensitive or high-churn files 377 | 378 | # Gradle 379 | 380 | # Gradle and Maven with auto-import 381 | # When using Gradle or Maven with auto-import, you should exclude module files, 382 | # since they will be recreated, and may cause churn. Uncomment if using 383 | # auto-import. 384 | # .idea/artifacts 385 | # .idea/compiler.xml 386 | # .idea/jarRepositories.xml 387 | # .idea/modules.xml 388 | # .idea/*.iml 389 | # .idea/modules 390 | # *.iml 391 | # *.ipr 392 | 393 | # CMake 394 | 395 | # Mongo Explorer plugin 396 | 397 | # File-based project format 398 | 399 | # IntelliJ 400 | 401 | # mpeltonen/sbt-idea plugin 402 | 403 | # JIRA plugin 404 | 405 | # Cursive Clojure plugin 406 | 407 | # Crashlytics plugin (for Android Studio and IntelliJ) 408 | 409 | # Editor-based Rest Client 410 | 411 | # Android studio 3.1+ serialized cache file 412 | 413 | ### PyCharm+all Patch ### 414 | # Ignores the whole .idea folder and all .iml files 415 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 416 | 417 | 418 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 419 | 420 | 421 | # Sonarlint plugin 422 | 423 | ### Python ### 424 | # Byte-compiled / optimized / DLL files 425 | 426 | # C extensions 427 | 428 | # Distribution / packaging 429 | 430 | # PyInstaller 431 | # Usually these files are written by a python script from a template 432 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 433 | 434 | # Installer logs 435 | 436 | # Unit test / coverage reports 437 | 438 | # Translations 439 | 440 | # Django stuff: 441 | 442 | # Flask stuff: 443 | 444 | # Scrapy stuff: 445 | 446 | # Sphinx documentation 447 | 448 | # PyBuilder 449 | 450 | # Jupyter Notebook 451 | 452 | # IPython 453 | 454 | # pyenv 455 | # For a library or package, you might want to ignore these files since the code is 456 | # intended to run in multiple environments; otherwise, check them in: 457 | # .python-version 458 | 459 | # pipenv 460 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 461 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 462 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 463 | # install all needed dependencies. 464 | 465 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 466 | 467 | # Celery stuff 468 | 469 | # SageMath parsed files 470 | 471 | # Environments 472 | 473 | # Spyder project settings 474 | 475 | # Rope project settings 476 | 477 | # mkdocs documentation 478 | 479 | # mypy 480 | 481 | # Pyre type checker 482 | 483 | # pytype static type analyzer 484 | 485 | # Cython debug symbols 486 | 487 | ### venv ### 488 | # Virtualenv 489 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 490 | [Bb]in 491 | [Ii]nclude 492 | [Ll]ib 493 | [Ll]ib64 494 | [Ll]ocal 495 | [Ss]cripts 496 | pyvenv.cfg 497 | pip-selfcheck.json 498 | 499 | ### Vim ### 500 | # Swap 501 | [._]*.s[a-v][a-z] 502 | !*.svg # comment out if you don't need vector files 503 | [._]*.sw[a-p] 504 | [._]s[a-rt-v][a-z] 505 | [._]ss[a-gi-z] 506 | [._]sw[a-p] 507 | 508 | # Session 509 | Session.vim 510 | Sessionx.vim 511 | 512 | # Temporary 513 | .netrwhist 514 | *~ 515 | # Auto-generated tag files 516 | tags 517 | # Persistent undo 518 | [._]*.un~ 519 | 520 | ### VirtualEnv ### 521 | # Virtualenv 522 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 523 | 524 | ### VisualStudioCode ### 525 | .vscode/* 526 | !.vscode/settings.json 527 | !.vscode/tasks.json 528 | !.vscode/launch.json 529 | !.vscode/extensions.json 530 | *.code-workspace 531 | 532 | # Local History for Visual Studio Code 533 | .history/ 534 | 535 | ### VisualStudioCode Patch ### 536 | # Ignore all local history of files 537 | .history 538 | .ionide 539 | 540 | ### vs ### 541 | ## Ignore Visual Studio temporary files, build results, and 542 | ## files generated by popular Visual Studio add-ons. 543 | ## 544 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 545 | 546 | # User-specific files 547 | *.rsuser 548 | *.suo 549 | *.user 550 | *.userosscache 551 | *.sln.docstates 552 | 553 | # User-specific files (MonoDevelop/Xamarin Studio) 554 | *.userprefs 555 | 556 | # Mono auto generated files 557 | mono_crash.* 558 | 559 | # Build results 560 | [Dd]ebug/ 561 | [Dd]ebugPublic/ 562 | [Rr]elease/ 563 | [Rr]eleases/ 564 | x64/ 565 | x86/ 566 | [Aa][Rr][Mm]/ 567 | [Aa][Rr][Mm]64/ 568 | bld/ 569 | [Bb]in/ 570 | [Oo]bj/ 571 | [Ll]og/ 572 | [Ll]ogs/ 573 | 574 | # Visual Studio 2015/2017 cache/options directory 575 | .vs/ 576 | # Uncomment if you have tasks that create the project's static files in wwwroot 577 | #wwwroot/ 578 | 579 | # Visual Studio 2017 auto generated files 580 | Generated\ Files/ 581 | 582 | # MSTest test Results 583 | [Tt]est[Rr]esult*/ 584 | [Bb]uild[Ll]og.* 585 | 586 | # NUnit 587 | *.VisualState.xml 588 | TestResult.xml 589 | nunit-*.xml 590 | 591 | # Build Results of an ATL Project 592 | [Dd]ebugPS/ 593 | [Rr]eleasePS/ 594 | dlldata.c 595 | 596 | # Benchmark Results 597 | BenchmarkDotNet.Artifacts/ 598 | 599 | # .NET Core 600 | project.lock.json 601 | project.fragment.lock.json 602 | artifacts/ 603 | 604 | # StyleCop 605 | StyleCopReport.xml 606 | 607 | # Files built by Visual Studio 608 | *_i.c 609 | *_p.c 610 | *_h.h 611 | *.ilk 612 | *.meta 613 | *.obj 614 | *.iobj 615 | *.pch 616 | *.ipdb 617 | *.pgc 618 | *.pgd 619 | *.rsp 620 | *.sbr 621 | *.tlb 622 | *.tli 623 | *.tlh 624 | *.tmp 625 | *.tmp_proj 626 | *_wpftmp.csproj 627 | *.vspscc 628 | *.vssscc 629 | .builds 630 | *.pidb 631 | *.svclog 632 | *.scc 633 | 634 | # Chutzpah Test files 635 | _Chutzpah* 636 | 637 | # Visual C++ cache files 638 | ipch/ 639 | *.aps 640 | *.ncb 641 | *.opendb 642 | *.opensdf 643 | *.sdf 644 | *.cachefile 645 | *.VC.db 646 | *.VC.VC.opendb 647 | 648 | # Visual Studio profiler 649 | *.psess 650 | *.vsp 651 | *.vspx 652 | *.sap 653 | 654 | # Visual Studio Trace Files 655 | *.e2e 656 | 657 | # TFS 2012 Local Workspace 658 | $tf/ 659 | 660 | # Guidance Automation Toolkit 661 | *.gpState 662 | 663 | # ReSharper is a .NET coding add-in 664 | _ReSharper*/ 665 | *.[Rr]e[Ss]harper 666 | *.DotSettings.user 667 | 668 | # TeamCity is a build add-in 669 | _TeamCity* 670 | 671 | # DotCover is a Code Coverage Tool 672 | *.dotCover 673 | 674 | # AxoCover is a Code Coverage Tool 675 | .axoCover/* 676 | !.axoCover/settings.json 677 | 678 | # Coverlet is a free, cross platform Code Coverage Tool 679 | coverage*[.json, .xml, .info] 680 | 681 | # Visual Studio code coverage results 682 | *.coverage 683 | *.coveragexml 684 | 685 | # NCrunch 686 | _NCrunch_* 687 | .*crunch*.local.xml 688 | nCrunchTemp_* 689 | 690 | # MightyMoose 691 | *.mm.* 692 | AutoTest.Net/ 693 | 694 | # Web workbench (sass) 695 | .sass-cache/ 696 | 697 | # Installshield output folder 698 | [Ee]xpress/ 699 | 700 | # DocProject is a documentation generator add-in 701 | DocProject/buildhelp/ 702 | DocProject/Help/*.HxT 703 | DocProject/Help/*.HxC 704 | DocProject/Help/*.hhc 705 | DocProject/Help/*.hhk 706 | DocProject/Help/*.hhp 707 | DocProject/Help/Html2 708 | DocProject/Help/html 709 | 710 | # Click-Once directory 711 | publish/ 712 | 713 | # Publish Web Output 714 | *.[Pp]ublish.xml 715 | *.azurePubxml 716 | # Note: Comment the next line if you want to checkin your web deploy settings, 717 | # but database connection strings (with potential passwords) will be unencrypted 718 | *.pubxml 719 | *.publishproj 720 | 721 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 722 | # checkin your Azure Web App publish settings, but sensitive information contained 723 | # in these scripts will be unencrypted 724 | PublishScripts/ 725 | 726 | # NuGet Packages 727 | *.nupkg 728 | # NuGet Symbol Packages 729 | *.snupkg 730 | # The packages folder can be ignored because of Package Restore 731 | **/[Pp]ackages/* 732 | # except build/, which is used as an MSBuild target. 733 | !**/[Pp]ackages/build/ 734 | # Uncomment if necessary however generally it will be regenerated when needed 735 | #!**/[Pp]ackages/repositories.config 736 | # NuGet v3's project.json files produces more ignorable files 737 | *.nuget.props 738 | *.nuget.targets 739 | 740 | # Microsoft Azure Build Output 741 | csx/ 742 | *.build.csdef 743 | 744 | # Microsoft Azure Emulator 745 | ecf/ 746 | rcf/ 747 | 748 | # Windows Store app package directories and files 749 | AppPackages/ 750 | BundleArtifacts/ 751 | Package.StoreAssociation.xml 752 | _pkginfo.txt 753 | *.appx 754 | *.appxbundle 755 | *.appxupload 756 | 757 | # Visual Studio cache files 758 | # files ending in .cache can be ignored 759 | *.[Cc]ache 760 | # but keep track of directories ending in .cache 761 | !?*.[Cc]ache/ 762 | 763 | # Others 764 | ClientBin/ 765 | ~$* 766 | *.dbmdl 767 | *.dbproj.schemaview 768 | *.jfm 769 | *.pfx 770 | *.publishsettings 771 | orleans.codegen.cs 772 | 773 | # Including strong name files can present a security risk 774 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 775 | #*.snk 776 | 777 | # Since there are multiple workflows, uncomment next line to ignore bower_components 778 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 779 | #bower_components/ 780 | 781 | # RIA/Silverlight projects 782 | Generated_Code/ 783 | 784 | # Backup & report files from converting an old project file 785 | # to a newer Visual Studio version. Backup files are not needed, 786 | # because we have git ;-) 787 | _UpgradeReport_Files/ 788 | Backup*/ 789 | UpgradeLog*.XML 790 | UpgradeLog*.htm 791 | ServiceFabricBackup/ 792 | *.rptproj.bak 793 | 794 | # SQL Server files 795 | *.mdf 796 | *.ldf 797 | *.ndf 798 | 799 | # Business Intelligence projects 800 | *.rdl.data 801 | *.bim.layout 802 | *.bim_*.settings 803 | *.rptproj.rsuser 804 | *- [Bb]ackup.rdl 805 | *- [Bb]ackup ([0-9]).rdl 806 | *- [Bb]ackup ([0-9][0-9]).rdl 807 | 808 | # Microsoft Fakes 809 | FakesAssemblies/ 810 | 811 | # GhostDoc plugin setting file 812 | *.GhostDoc.xml 813 | 814 | # Node.js Tools for Visual Studio 815 | .ntvs_analysis.dat 816 | node_modules/ 817 | 818 | # Visual Studio 6 build log 819 | *.plg 820 | 821 | # Visual Studio 6 workspace options file 822 | *.opt 823 | 824 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 825 | *.vbw 826 | 827 | # Visual Studio LightSwitch build output 828 | **/*.HTMLClient/GeneratedArtifacts 829 | **/*.DesktopClient/GeneratedArtifacts 830 | **/*.DesktopClient/ModelManifest.xml 831 | **/*.Server/GeneratedArtifacts 832 | **/*.Server/ModelManifest.xml 833 | _Pvt_Extensions 834 | 835 | # Paket dependency manager 836 | .paket/paket.exe 837 | paket-files/ 838 | 839 | # FAKE - F# Make 840 | .fake/ 841 | 842 | # CodeRush personal settings 843 | .cr/personal 844 | 845 | # Python Tools for Visual Studio (PTVS) 846 | 847 | # Cake - Uncomment if you are using it 848 | # tools/** 849 | # !tools/packages.config 850 | 851 | # Tabs Studio 852 | *.tss 853 | 854 | # Telerik's JustMock configuration file 855 | *.jmconfig 856 | 857 | # BizTalk build output 858 | *.btp.cs 859 | *.btm.cs 860 | *.odx.cs 861 | *.xsd.cs 862 | 863 | # OpenCover UI analysis results 864 | OpenCover/ 865 | 866 | # Azure Stream Analytics local run output 867 | ASALocalRun/ 868 | 869 | # MSBuild Binary and Structured Log 870 | *.binlog 871 | 872 | # NVidia Nsight GPU debugger configuration file 873 | *.nvuser 874 | 875 | # MFractors (Xamarin productivity tool) working folder 876 | .mfractor/ 877 | 878 | # Local History for Visual Studio 879 | .localhistory/ 880 | 881 | # BeatPulse healthcheck temp database 882 | healthchecksdb 883 | 884 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 885 | MigrationBackup/ 886 | 887 | # Ionide (cross platform F# VS Code tools) working folder 888 | .ionide/ 889 | 890 | ### VisualStudio ### 891 | 892 | # User-specific files 893 | 894 | # User-specific files (MonoDevelop/Xamarin Studio) 895 | 896 | # Mono auto generated files 897 | 898 | # Build results 899 | [Ww][Ii][Nn]32/ 900 | 901 | # Visual Studio 2015/2017 cache/options directory 902 | # Uncomment if you have tasks that create the project's static files in wwwroot 903 | 904 | # Visual Studio 2017 auto generated files 905 | 906 | # MSTest test Results 907 | 908 | # NUnit 909 | 910 | # Build Results of an ATL Project 911 | 912 | # Benchmark Results 913 | 914 | # .NET Core 915 | 916 | # ASP.NET Scaffolding 917 | ScaffoldingReadMe.txt 918 | 919 | # StyleCop 920 | 921 | # Files built by Visual Studio 922 | *.tlog 923 | 924 | # Chutzpah Test files 925 | 926 | # Visual C++ cache files 927 | 928 | # Visual Studio profiler 929 | 930 | # Visual Studio Trace Files 931 | 932 | # TFS 2012 Local Workspace 933 | 934 | # Guidance Automation Toolkit 935 | 936 | # ReSharper is a .NET coding add-in 937 | 938 | # TeamCity is a build add-in 939 | 940 | # DotCover is a Code Coverage Tool 941 | 942 | # AxoCover is a Code Coverage Tool 943 | 944 | # Coverlet is a free, cross platform Code Coverage Tool 945 | coverage*.json 946 | coverage*.xml 947 | coverage*.info 948 | 949 | # Visual Studio code coverage results 950 | 951 | # NCrunch 952 | 953 | # MightyMoose 954 | 955 | # Web workbench (sass) 956 | 957 | # Installshield output folder 958 | 959 | # DocProject is a documentation generator add-in 960 | 961 | # Click-Once directory 962 | 963 | # Publish Web Output 964 | # Note: Comment the next line if you want to checkin your web deploy settings, 965 | # but database connection strings (with potential passwords) will be unencrypted 966 | 967 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 968 | # checkin your Azure Web App publish settings, but sensitive information contained 969 | # in these scripts will be unencrypted 970 | 971 | # NuGet Packages 972 | # NuGet Symbol Packages 973 | # The packages folder can be ignored because of Package Restore 974 | # except build/, which is used as an MSBuild target. 975 | # Uncomment if necessary however generally it will be regenerated when needed 976 | # NuGet v3's project.json files produces more ignorable files 977 | 978 | # Nuget personal access tokens and Credentials 979 | nuget.config 980 | 981 | # Microsoft Azure Build Output 982 | 983 | # Microsoft Azure Emulator 984 | 985 | # Windows Store app package directories and files 986 | 987 | # Visual Studio cache files 988 | # files ending in .cache can be ignored 989 | # but keep track of directories ending in .cache 990 | 991 | # Others 992 | 993 | # Including strong name files can present a security risk 994 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 995 | 996 | # Since there are multiple workflows, uncomment next line to ignore bower_components 997 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 998 | 999 | # RIA/Silverlight projects 1000 | 1001 | # Backup & report files from converting an old project file 1002 | # to a newer Visual Studio version. Backup files are not needed, 1003 | # because we have git ;-) 1004 | 1005 | # SQL Server files 1006 | 1007 | # Business Intelligence projects 1008 | 1009 | # Microsoft Fakes 1010 | 1011 | # GhostDoc plugin setting file 1012 | 1013 | # Node.js Tools for Visual Studio 1014 | 1015 | # Visual Studio 6 build log 1016 | 1017 | # Visual Studio 6 workspace options file 1018 | 1019 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 1020 | 1021 | # Visual Studio LightSwitch build output 1022 | 1023 | # Paket dependency manager 1024 | 1025 | # FAKE - F# Make 1026 | 1027 | # CodeRush personal settings 1028 | 1029 | # Python Tools for Visual Studio (PTVS) 1030 | 1031 | # Cake - Uncomment if you are using it 1032 | # tools/** 1033 | # !tools/packages.config 1034 | 1035 | # Tabs Studio 1036 | 1037 | # Telerik's JustMock configuration file 1038 | 1039 | # BizTalk build output 1040 | 1041 | # OpenCover UI analysis results 1042 | 1043 | # Azure Stream Analytics local run output 1044 | 1045 | # MSBuild Binary and Structured Log 1046 | 1047 | # NVidia Nsight GPU debugger configuration file 1048 | 1049 | # MFractors (Xamarin productivity tool) working folder 1050 | 1051 | # Local History for Visual Studio 1052 | 1053 | # BeatPulse healthcheck temp database 1054 | 1055 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 1056 | 1057 | # Ionide (cross platform F# VS Code tools) working folder 1058 | 1059 | # Fody - auto-generated XML schema 1060 | FodyWeavers.xsd 1061 | 1062 | # VS Code files for those working on multiple tools 1063 | 1064 | # Local History for Visual Studio Code 1065 | 1066 | # Windows Installer files from build outputs 1067 | *.cab 1068 | *.msi 1069 | *.msix 1070 | *.msm 1071 | *.msp 1072 | 1073 | # JetBrains Rider 1074 | *.sln.iml 1075 | 1076 | ### VisualStudio Patch ### 1077 | # Additional files built by Visual Studio 1078 | 1079 | # End of https://www.toptal.com/developers/gitignore/api/python,venv,virtualenv,django,vs,visualstudio,visualstudiocode,pycharm+all,phpstorm+all,vim,database,git,gis,gitbook,direnv,dotenv,jenv,jetbrains+all -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[python]": { 3 | "editor.defaultFormatter": "ms-python.autopep8" 4 | }, 5 | "python.linting.pylintArgs": ["--disable=C0114,C0115"], 6 | "python.formatting.provider": "none" 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bambier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include pwa/static * 2 | recursive-include pwa/templates * 3 | recursive-include pwa/templatetags * 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ===== 2 | PWA 3 | ===== 4 | 5 | 6 | ![PyPI - Versions from Framework Classifiers](https://img.shields.io/pypi/frameworkversions/django/django-simple-pwa) 7 | ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/django-simple-pwa) 8 | ![PyPI - Implementation](https://img.shields.io/pypi/implementation/django-simple-pwa) 9 | ![PyPI - Wheel](https://img.shields.io/pypi/wheel/django-simple-pwa) 10 | 11 | 12 | 13 | PWA 14 | ### 15 | 16 | PWA is a simple Django app to develop and deploy a Progressive Web Application. 17 | 18 | Detailed documentation is in the "docs" directory. 19 | 20 | Quick start 21 | ----------- 22 | 23 | 1. Add "pwa" to your INSTALLED_APPS setting like this:: 24 | 25 | INSTALLED_APPS = [ 26 | ..., 27 | 'pwa', 28 | ] 29 | 30 | 2. Include the pwa URLconf in your project urls.py like this:: 31 | 32 | path('', include('pwa.urls')), 33 | 34 | 3. Run ``python manage.py collectstatic`` to get the PWA static files. 35 | 36 | 4. Load pwa & its meta files in index HTML file like as:: 37 | 38 | 39 | {% load pwa %} 40 | {% pwa_meta_data %} 41 | {% pwa_meta_script %} 42 | 43 | 5. Start the development server on HTTPS or localhost to see installable app. 44 | 45 | 46 | 47 | 48 | github: https://github.com/bambier/django-simple-pwa 49 | 50 | 51 | documents: https://django-simple-pwa.readthedocs.io/fa/latest/ 52 | 53 | 54 | 55 | pypi: https://pypi.org/project/django-simple-pwa/ 56 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/locales/en_US/LC_MESSAGES/customization.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2021, Nima Esmaeili 3 | # This file is distributed under the same license as the django-simple-pwa 4 | # package. 5 | # FIRST AUTHOR , 2022. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: django-simple-pwa \n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2022-10-02 17:14+0330\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.9.1\n" 20 | 21 | #: ../../source/customization.rst:4 22 | msgid "شخصی سازی" 23 | msgstr "Customization" 24 | 25 | #: ../../source/customization.rst:7 26 | msgid "ساختار پیش فرض" 27 | msgstr "Default structure" 28 | 29 | #: ../../source/customization.rst:9 30 | msgid "DSP از یک ساختار پیشفرض برای تولید ``manifest.json`` استفاده می‌کند." 31 | msgstr "DSP uses a default structure for generation ``manifest.json``." 32 | 33 | #: ../../source/customization.rst:83 34 | msgid "تغییر پیش فرض" 35 | msgstr "Change the defaults" 36 | 37 | #: ../../source/customization.rst:84 38 | msgid "" 39 | "برای شخصی‌سازی فایل ``manifest.json`` ``app.js`` ``sw.js`` کافیست متغیر " 40 | "``PWA_CONFIG`` ``PWA_SW`` ``PWA_APP`` را در فایل ``settings.py`` تعریف " 41 | "کنید." 42 | msgstr "To customize the ``manifest.json`` ``app.js`` ``sw.js`` files, just define the ``PWA_CONFIG`` ``PWA_SW`` ``PWA_APP`` variables in the ``settings.py`` file." 43 | 44 | #: ../../source/customization.rst:102 45 | msgid "توجه داشته باشید که هیچ یک از دو آیکون شما دارای سایز برابر نباشند." 46 | msgstr "Note that none of your two icons have the same size." 47 | 48 | #: ../../source/customization.rst:104 49 | msgid "برای مثال ساختار زیر باعث ایجاد خطا در برنامه میشود." 50 | msgstr "For example, the following structure causes an error in the program." 51 | 52 | #: ../../source/customization.rst:125 53 | msgid "این بدین معنی است که لزوما تغییر فرمت موجب جلوگیری از خطا نمی‌شود." 54 | msgstr "This means that changing the format does not necessarily prevent the error." 55 | 56 | #: ../../source/customization.rst:128 57 | msgid "" 58 | "همچنین ما توصیه نمی‌کنیم که ``PWA_APP`` و ``PWA_SW`` را دوباره تعریف کنید" 59 | " مگر آنکه بدانید واقعا چه می‌کنید." 60 | msgstr "Also, we don't recommend redefining ``PWA_APPand`` ``PWA_SW`` unless you know what you're actually doing." 61 | 62 | #: ../../source/customization.rst:131 63 | msgid "" 64 | "شما میتوانید آیکون‌های خود را در سایزهای مختلف با فرمت‌های دلخواه خود به " 65 | "صورت فوق تعریف کنید اما ما توصیه میکنیم از فرمت .ico استفاده کنید." 66 | msgstr "You can define your icons in different sizes with your desired formats as above, but we recommend using the .ico format." 67 | 68 | #: ../../source/customization.rst:135 69 | msgid "تغییر ``manifest.json``" 70 | msgstr "change ``manifest.json``" 71 | 72 | #: ../../source/customization.rst:137 73 | msgid "برای این کار ``PWA_CONFIG`` را به ``settings.py`` اضافه کنید." 74 | msgstr "For this, add ``PWA_CONFIG`` to ypur ``settings.py``" 75 | 76 | #: ../../source/customization.rst:210 77 | msgid "مقادیر قابل قبول ``manifest.json``" 78 | msgstr "acceptable values ``manifest.json``" 79 | 80 | #: ../../source/customization.rst:211 81 | msgid "" 82 | "ما در اینجا برخی از مواردی که یک PWA میتواند در فایل ``manifest.json`` " 83 | "خود داشته باشد را به صورت پیشفرض برای اپ خود تعریف کرده‌ایم و در ادامه به" 84 | " تعریف مقادیری که میتوانید برای آن‌ها لحاظ کنید می‌پردازیم." 85 | msgstr "Here, we have defined some of the things that a PWA can have in its ``manifest.json`` file by default, and we will continue to describe the values that you can put in for them." 86 | 87 | #: ../../source/customization.rst:242 88 | msgid "" 89 | "برای اطلاعات بیشتر می‌توانید به برخی مستندات آن که در لیست زیر فراهم " 90 | "کردیم بپردازید:" 91 | msgstr "For more information, you can refer to some of its documentation that we have provided in the list below:" 92 | 93 | #: ../../source/customization.rst:244 94 | msgid "" 95 | "`developer.mozilla.org `_" 97 | msgstr "" 98 | 99 | #: ../../source/customization.rst:245 100 | msgid "" 101 | "`developer.chrome.com " 102 | "`_" 103 | msgstr "" 104 | 105 | #: ../../source/customization.rst:246 106 | msgid "`web.dev `_" 107 | msgstr "" 108 | 109 | #: ../../source/customization.rst:251 110 | msgid "تغییر ``ServiceWorker.js``" 111 | msgstr "change ``ServiceWorker.js``" 112 | 113 | #: ../../source/customization.rst:253 114 | msgid "" 115 | "برای این کار ``PWA_SW`` را در ``settings.py`` به یکی از دو روش زیر تعریف " 116 | "کنید." 117 | msgstr "To do this , define ``PWA_SW`` in ``settings.py`` one of the following two ways." 118 | 119 | #: ../../source/customization.rst:271 120 | msgid "" 121 | "برای اطلاعات بیشتر نسبت به نحوه‌ی کارکرد ``ServiceWorker.js`` می‌توانید " 122 | "از منابع زیر استفاده کنید." 123 | msgstr "For more information on how it works ``ServiceWorker.js``, you can use the following sources." 124 | 125 | #: ../../source/customization.rst:273 126 | msgid "" 127 | "`developer.mozilla.org `_" 129 | msgstr "" 130 | 131 | #: ../../source/customization.rst:274 132 | msgid "" 133 | "`developers.google.com " 134 | "`_" 136 | msgstr "" 137 | 138 | #: ../../source/customization.rst:275 139 | msgid "" 140 | "`docs.microsoft.com `_" 142 | msgstr "" 143 | 144 | #: ../../source/customization.rst:282 145 | msgid "تغییر ``app.js``" 146 | msgstr "change ``app.js``" 147 | 148 | #: ../../source/customization.rst:284 149 | msgid "" 150 | "برای این کار کافیست متغییر ``PWA_APP`` را در فایل ``settings.py`` تعریف " 151 | "کنید." 152 | msgstr "For this, just define the variable ``PWA_APP`` in the file ``settings.py``." 153 | 154 | #: ../../source/customization.rst:304 155 | msgid "" 156 | "همچنان توصیه نمی‌کنیم که ``PWA_APP`` و ``PWA_SW`` را دوباره تعریف کنید " 157 | "مگر آن که بدانید واقعا چه می‌کنید." 158 | msgstr "We still don't recommend redefining ``PWA_SW`` unless you know what you're really doing." 159 | 160 | #: ../../source/customization.rst:309 161 | msgid "تغییر محتوای صفحه‌ی آفلاین" 162 | msgstr "Change offline page content" 163 | 164 | #: ../../source/customization.rst:310 165 | msgid "" 166 | "ما از ساختاری مشابه ساختار جنگو برای تغییر صفحه‌ی آفلاین استفاده می‌کنیم." 167 | " به طوری که برای تغییر آن می‌بایست ابتدا در مسیر ``/pwa/``" 168 | " یک فایل با نام ``offline.html`` ایجاد کنید و سپس آن را در مسیر " 169 | "``pwa/pwa_offline.html`` اکستند کنید." 170 | msgstr "We use a structure similar to Django's structure to change the offline page. So, to change it, you must first create ``offline.html`` file in ``/pwa/`` path and then extend it in to the path ``pwa/pwa_offline.html``." 171 | 172 | #: ../../source/customization.rst:326 173 | msgid "" 174 | "ما به طور پیش‌فرض از `بوت‌استرپ `_ در قسمت " 175 | "آفلاین سایت استفاده می‌کنیم با این حال میتوانید به صورت زیر فایل‌های css " 176 | "و js خود را در آن بارگذاری کنید." 177 | msgstr "By default, we use `bootstrap `_ in the offline part of the site, however, you can upload your css and js files in it as below." 178 | 179 | #: ../../source/customization.rst:337 180 | msgid "" 181 | "توجه داشته باشید با اعمال قطعه کد بالا دیگر بوت استرپ در صفحه اعمال " 182 | "نخواهد شد." 183 | msgstr "Note that by applying the code above, Bootstrap will no longer be applied to the page." 184 | 185 | #: ../../source/customization.rst:339 186 | msgid "" 187 | "درضمن هرگونه تغییر در فایل آفلاین باید با تغییر کش در " 188 | "``ServiceWorker.js`` همراه باشد. بنابراین در صورتی که فایل آفلاین را " 189 | "تغییر دادید نیاز است که ``PWA_SW`` را نیز تعریف کنید هرچند ممکن است بدون" 190 | " این کار هم برنامه کار بکند ولی بهتر است که ``ServiceWorker.js`` باز " 191 | "نویسی شود." 192 | msgstr "In addition, any change in the offline file must be accompanied by a changing cache in ``ServiceWorker.js``. Therefore, if you changed the offline file, you need to define ``PWA_SW``, although the program may work without this, but it is better to rewrite ``ServiceWorker.js``." 193 | 194 | -------------------------------------------------------------------------------- /docs/locales/en_US/LC_MESSAGES/errors.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2021, Nima Esmaeili 3 | # This file is distributed under the same license as the django-simple-pwa 4 | # package. 5 | # FIRST AUTHOR , 2022. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: django-simple-pwa \n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2022-10-02 17:34+0330\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.9.1\n" 20 | 21 | #: ../../source/errors.rst:5 22 | msgid "خطا و ارورها" 23 | msgstr "Errors and bugs" 24 | 25 | #: ../../source/errors.rst:8 26 | msgid "" 27 | "در صورتی که با خطای زیر مواجه شدید آیکون‌های خود را به صورت صحیح آدرس دهی" 28 | " کنید. همچنین مطمئن شوید هیچ یک از دو آیکون شما دارای سایز برابر " 29 | "نمی‌باشند حتی اگر فرمت متفاوتی دارند." 30 | msgstr "" 31 | "If you encounter the following error, address your icons correctly. Also," 32 | " make sure that none of your two icons are the same size, even if they " 33 | "are in different formats." 34 | 35 | msgid "error.png" 36 | msgstr "" 37 | 38 | #: ../../source/errors.rst:18 39 | msgid "عدم نمایش دکمه نصب" 40 | msgstr "" 41 | 42 | #: ../../source/errors.rst:21 43 | msgid "" 44 | "در صورتی که دکمه نصب نمایش داده نشد، مطمئن شوید که سرور بر روی لوکال هاست" 45 | " و یا HTTPS اجرا میشود. اجرای سرور بر روی شبکه NAT نیازمند دارا بودن " 46 | "گواهی SSL معتبر است. این موضوع به django-simple-pwa مربوط نیست و به " 47 | "ساختار PWA مربوط می‌شود." 48 | msgstr "If the installation button is not displayed, make sure the server runs on the locals or the HTTPS. Running the server on the NAT network requires a valid SSL certificate. This is not related to django-simple-pwa and is related to the PWA structure." 49 | 50 | -------------------------------------------------------------------------------- /docs/locales/en_US/LC_MESSAGES/index.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2021, Nima Esmaeili 3 | # This file is distributed under the same license as the django-simple-pwa 4 | # package. 5 | # FIRST AUTHOR , 2022. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: django-simple-pwa \n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2022-10-02 17:14+0330\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.9.1\n" 20 | 21 | #: ../../source/index.rst:18 22 | msgid "لیست" 23 | msgstr "List" 24 | 25 | #: ../../source/index.rst:3 26 | msgid "django-simple-pwa" 27 | msgstr "django-simple-pwa" 28 | 29 | #: ../../source/index.rst:6 30 | msgid "این اپ یک راهکار سریع برای ایجاد و کنترل اپ PWA برای وب سایت شماست." 31 | msgstr "This app is a quick solution to create and control a PWA app for your website." 32 | 33 | #: ../../source/index.rst:8 34 | msgid "لطفا ما را برای ترجمه‌ی مستندات به زبان محلی خود یاری کنید." 35 | msgstr "Please help us to translate the documents into your local language." 36 | 37 | #: ../../source/index.rst:11 38 | msgid "فهرست محتوا" 39 | msgstr "Table of Contents" 40 | 41 | #: ../../source/index.rst:13 42 | msgid ":ref:`quickstart`" 43 | msgstr "" 44 | 45 | #: ../../source/index.rst:14 46 | msgid ":ref:`customization`" 47 | msgstr "" 48 | 49 | #: ../../source/index.rst:15 50 | msgid ":ref:`errors`" 51 | msgstr "" 52 | 53 | -------------------------------------------------------------------------------- /docs/locales/en_US/LC_MESSAGES/quickstart.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2021, Nima Esmaeili 3 | # This file is distributed under the same license as the django-simple-pwa 4 | # package. 5 | # FIRST AUTHOR , 2022. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: django-simple-pwa \n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2022-10-02 17:14+0330\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.9.1\n" 20 | 21 | #: ../../source/quickstart.rst:4 22 | msgid "شروع سریع" 23 | msgstr "Quick Start" 24 | 25 | #: ../../source/quickstart.rst:8 26 | msgid "نصب" 27 | msgstr "Installation" 28 | 29 | #: ../../source/quickstart.rst:9 30 | msgid "ابتدا DSP (django-simple-pwa) را با دستور زیر نصب کنید" 31 | msgstr "First, install DSP (django-simple-pwa) with the following command" 32 | 33 | #: ../../source/quickstart.rst:18 34 | msgid "حال DSP را به اپ‌های نصب شده اضافه کنید." 35 | msgstr "Now add DSP to installed apps." 36 | 37 | #: ../../source/quickstart.rst:33 38 | msgid "تنظیمات" 39 | msgstr "Settings" 40 | 41 | #: ../../source/quickstart.rst:34 42 | msgid "" 43 | "حال urlهای DSP را به ``urls.py`` که در کنار ``setting.py`` واقع است به " 44 | "شکل زیر اضافه کنید." 45 | msgstr "Now add the DSP urls to ``urls.py`` the next one to the ``setting.py`` as shown below." 46 | 47 | #: ../../source/quickstart.rst:51 48 | msgid "مهم است که محل فایل‌های استاتیک را در تنظمیات خود معرفی کنید." 49 | msgstr "It is important to specify the location of static files in your settings." 50 | 51 | #: ../../source/quickstart.rst:64 52 | msgid "همچنین فرمان ``./manage.py collectstatic`` را اجرا کنید." 53 | msgstr "Also run the command ``../manage.py collectstatic``" 54 | 55 | #: ../../source/quickstart.rst:69 56 | msgid "اجرا در لوکال هاست" 57 | msgstr "Run on localhost" 58 | 59 | #: ../../source/quickstart.rst:71 60 | msgid "" 61 | "در صورتی که پروژه‌ را در لوکال هاست خود اجرا می‌کنید، مطمئن شوید که " 62 | "فایل‌های استاتیک به درستی لود شده‌اند." 63 | msgstr "If you are running the project on your localhost, make sure that the static files are loaded correctly." 64 | 65 | #: ../../source/quickstart.rst:88 66 | msgid "لود کردن فایل‌های ضروری" 67 | msgstr "Loading the necessary files" 68 | 69 | #: ../../source/quickstart.rst:90 70 | msgid "" 71 | "حال در فایل html اصلی خود که عموما به نام ``index.html`` شناخته می‌شود، " 72 | "متا دیتا‌های اپ را لود کنید." 73 | msgstr "Now in your main html file commonly known as ``index.html``, load the meta data of app." 74 | 75 | #: ../../source/quickstart.rst:108 76 | msgid "" 77 | "همچین می‌توانید این تنظیمات را در تمام صفحات اعمال کنید تا از طریق همه " 78 | "آن‌ها pwa در دسترس باشد." 79 | msgstr "You can also apply this setting to all pages so that pwa is available through all of them." 80 | 81 | #: ../../source/quickstart.rst:112 82 | msgid "" 83 | "اگر همه مراحل را به درستی انجام داده باشید میتوانید اکنون سایت خود را بر " 84 | "روی دستگاه خود به صورت pwa نصب کنید." 85 | msgstr "If you have done all the steps correctly, you can now install your site on your device as pwa." 86 | 87 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/requirements.txt -------------------------------------------------------------------------------- /docs/source/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | direction: rtl; 3 | text-align: justify; 4 | } 5 | 6 | div.wy-nav-side { 7 | z-index: 0 !important; 8 | } 9 | 10 | .wy-nav-content { 11 | max-width: 98%; 12 | margin-bottom: 2%; 13 | margin-top: 2%; 14 | margin-right: auto; 15 | margin-left: auto; 16 | border: 1px solid; 17 | border-radius: 10px; 18 | box-shadow: 5px 10px 10px 2px #6d6969; 19 | padding: 5%; 20 | } 21 | 22 | 23 | code, pre, span.pre, embed, code.docutils.literal.notranslate, p code.docutils.literal.notranslate { 24 | direction: ltr !important; 25 | text-align: left !important; 26 | justify-content: left !important; 27 | justify-items: left !important; 28 | justify-self: left !important; 29 | } 30 | 31 | 32 | div.admonition.note ul.simple li { 33 | margin: auto !important; 34 | margin-right: 50px !important; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /docs/source/_static/fonts/css/sahel.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: Sahel; 3 | src: url('../sahel/Sahel-FD.eot'); 4 | src: url('../sahel/Sahel-FD.eot?#iefix') format('embedded-opentype'), 5 | url('../sahel/Sahel-FD.woff2') format('woff2'), 6 | url('../sahel/Sahel-FD.woff') format('woff'), 7 | url('../sahel/Sahel-FD.ttf') format('truetype'); 8 | font-weight: normal; 9 | } 10 | 11 | @font-face { 12 | font-family: Sahel; 13 | src: url('../sahel/Sahel-Bold-FD.eot'); 14 | src: url('../sahel/Sahel-Bold-FD.eot?#iefix') format('embedded-opentype'), 15 | url('../sahel/Sahel-Bold-FD.woff2') format('woff2'), 16 | url('../sahel/Sahel-Bold-FD.woff') format('woff'), 17 | url('../sahel/Sahel-Bold-FD.ttf') format('truetype'); 18 | font-weight: bold; 19 | } 20 | 21 | @font-face { 22 | font-family: Sahel; 23 | src: url('../sahel/Sahel-Light-FD.eot'); 24 | src: url('../sahel/Sahel-Light-FD.eot?#iefix') format('embedded-opentype'), 25 | url('../sahel/Sahel-Light-FD.woff2') format('woff2'), 26 | url('../sahel/Sahel-Light-FD.woff') format('woff'), 27 | url('../sahel/Sahel-Light-FD.ttf') format('truetype'); 28 | font-weight: 300; 29 | } 30 | 31 | @font-face { 32 | font-family: Sahel; 33 | src: url('../sahel/Sahel-SemiBold-FD.eot'); 34 | src: url('../sahel/Sahel-SemiBold-FD.eot?#iefix') format('embedded-opentype'), 35 | url('../sahel/Sahel-SemiBold-FD.woff2') format('woff2'), 36 | url('../sahel/Sahel-SemiBold-FD.woff') format('woff'), 37 | url('../sahel/Sahel-SemiBold-FD.ttf') format('truetype'); 38 | font-weight: 600; 39 | } 40 | 41 | @font-face { 42 | font-family: Sahel; 43 | src: url('../sahel/Sahel-Black-FD.eot'); 44 | src: url('../sahel/Sahel-Black-FD.eot?#iefix') format('embedded-opentype'), 45 | url('../sahel/Sahel-Black-FD.woff2') format('woff2'), 46 | url('../sahel/Sahel-Black-FD.woff') format('woff'), 47 | url('../sahel/Sahel-Black-FD.ttf') format('truetype'); 48 | font-weight: 900; 49 | } 50 | 51 | 52 | .fn, .sahel { 53 | font-family: Sahel !important; 54 | } 55 | 56 | body, p, div, a, button, h1, h2, h3, h4, h5, h6, input, label, li, object, ol, option, table, tbody, thead, th, td, tr, tfoot, textarea, ul, header, footer, main, form, figcaption, caption { 57 | font-family: Sahel; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Black-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Black-FD.eot -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Black-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Black-FD.ttf -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Black-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Black-FD.woff -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Black-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Black-FD.woff2 -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Bold-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Bold-FD.eot -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Bold-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Bold-FD.ttf -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Bold-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Bold-FD.woff -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Bold-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Bold-FD.woff2 -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-FD.eot -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-FD.ttf -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-FD.woff -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-FD.woff2 -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Light-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Light-FD.eot -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Light-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Light-FD.ttf -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Light-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Light-FD.woff -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-Light-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-Light-FD.woff2 -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-SemiBold-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-SemiBold-FD.eot -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-SemiBold-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-SemiBold-FD.ttf -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-SemiBold-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-SemiBold-FD.woff -------------------------------------------------------------------------------- /docs/source/_static/fonts/sahel/Sahel-SemiBold-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/_static/fonts/sahel/Sahel-SemiBold-FD.woff2 -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'django-simple-pwa' 21 | copyright = '2021, Nima Esmaeili' 22 | author = 'Nima Esmaeili' 23 | 24 | # The full version, including alpha/beta/rc tags 25 | release = '2021' 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | ] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # The language for content autogenerated by Sphinx. Refer to documentation 40 | # for a list of supported languages. 41 | # 42 | # This is also used if you do content translation via gettext catalogs. 43 | # Usually you set "language" from the command line for these cases. 44 | language = 'fa' 45 | 46 | 47 | # List of patterns, relative to source directory, that match files and 48 | # directories to ignore when looking for source files. 49 | # This pattern also affects html_static_path and html_extra_path. 50 | exclude_patterns = [] 51 | 52 | 53 | # -- Options for HTML output ------------------------------------------------- 54 | 55 | # The theme to use for HTML and HTML Help pages. See the documentation for 56 | # a list of builtin themes. 57 | # 58 | html_theme = 'sphinx_rtd_theme' 59 | # Add any paths that contain custom static files (such as style sheets) here, 60 | # relative to this directory. They are copied after the builtin static files, 61 | # so a file named "default.css" will overwrite the builtin "default.css". 62 | html_static_path = ['_static'] 63 | 64 | 65 | html_css_files = [ 66 | 'css/custom.css', 67 | 'fonts/css/sahel.css', 68 | ] 69 | 70 | html_font_files = [ 71 | 'fonts/sahel/Sahel-Black-FD.eot', 72 | 'fonts/sahel/Sahel-Black-FD.ttf', 73 | 'fonts/sahel/Sahel-Black-FD.woff', 74 | 'fonts/sahel/Sahel-Black-FD.woff2', 75 | 76 | 'fonts/sahel/Sahel-Bold-FD.eot', 77 | 'fonts/sahel/Sahel-Bold-FD.ttf', 78 | 'fonts/sahel/Sahel-Bold-FD.woff', 79 | 'fonts/sahel/Sahel-Bold-FD.woff2', 80 | 81 | 'fonts/sahel/Sahel-FD.eot', 82 | 'fonts/sahel/Sahel-FD.ttf', 83 | 'fonts/sahel/Sahel-FD.woff', 84 | 'fonts/sahel/Sahel-FD.woff2', 85 | 86 | 'fonts/sahel/Sahel-Light-FD.eot', 87 | 'fonts/sahel/Sahel-Light-FD.ttf', 88 | 'fonts/sahel/Sahel-Light-FD.woff', 89 | 'fonts/sahel/Sahel-Light-FD.woff2', 90 | 91 | 'fonts/sahel/Sahel-SemiBold-FD.eot', 92 | 'fonts/sahel/Sahel-SemiBold-FD.ttf', 93 | 'fonts/sahel/Sahel-SemiBold-FD.woff', 94 | 'fonts/sahel/Sahel-SemiBold-FD.woff2', 95 | ] 96 | 97 | 98 | 99 | extensions = [ 100 | 'sphinx.ext.autosectionlabel', 101 | ] -------------------------------------------------------------------------------- /docs/source/customization.rst: -------------------------------------------------------------------------------- 1 | .. _customization: 2 | 3 | شخصی سازی 4 | ================== 5 | 6 | ساختار پیش فرض 7 | --------------------- 8 | 9 | DSP از یک ساختار پیشفرض برای تولید ``manifest.json`` استفاده می‌کند. 10 | 11 | 12 | .. code-block:: python 13 | :linenos: 14 | 15 | # 16 | 17 | DEFAULT_CONFIG = { 18 | "name": "Progressive Web Application", 19 | "short_name": "PWA", 20 | "theme_color": "#7820f5", 21 | "background_color": "#7820f5", 22 | "display": "standalone", 23 | "orientation": "portrait", 24 | "scope": "/", 25 | "start_url": "/", 26 | "icons": [ 27 | { 28 | "src": "/static/pwa/icons/72x72.png", 29 | "type": "image/png", 30 | "sizes": "72x72" 31 | }, 32 | { 33 | "src": "/static/pwa/icons/96x96.png", 34 | "type": "image/png", 35 | "sizes": "96x96" 36 | }, 37 | { 38 | "src": "/static/pwa/icons/128x128.png", 39 | "type": "image/png", 40 | "sizes": "128x128" 41 | }, 42 | { 43 | "src": "/static/pwa/icons/144x144.png", 44 | "type": "image/png", 45 | "sizes": "144x144" 46 | }, 47 | { 48 | "src": "/static/pwa/icons/152x152.png", 49 | "type": "image/png", 50 | "sizes": "152x152" 51 | }, 52 | { 53 | "src": "/static/pwa/icons/192x192.png", 54 | "type": "image/png", 55 | "sizes": "192x192" 56 | }, 57 | { 58 | "src": "/static/pwa/icons/384x384.png", 59 | "type": "image/png", 60 | "sizes": "384x384" 61 | }, 62 | { 63 | "src": "/static/pwa/icons/512x512.png", 64 | "type": "image/png", 65 | "sizes": "512x512" 66 | } 67 | ], 68 | "lang": "en", 69 | "dir": "ltr", 70 | "description": "Progressive Web app powerd by Django", 71 | "version": "1.", 72 | "manifest_version": "1.0", 73 | "permissions": [ 74 | "notifications", 75 | "webRequest" 76 | ], 77 | "author": "PWA-django" 78 | } 79 | 80 | 81 | 82 | تغییر پیش فرض 83 | ----------------------- 84 | برای شخصی‌سازی فایل ``manifest.json`` ``app.js`` ``sw.js`` کافیست متغیر ``PWA_CONFIG`` ``PWA_SW`` ``PWA_APP`` را در فایل ``settings.py`` تعریف کنید. 85 | 86 | 87 | .. code-block:: python 88 | :linenos: 89 | 90 | # 91 | 92 | PWA_CONFIG = { 93 | # ... 94 | "name": "Progressive Web Application" 95 | # ... 96 | } 97 | PWA_APP = """// js code here""" 98 | PWA_SW = """// js code here""" 99 | 100 | 101 | .. warning:: 102 | توجه داشته باشید که هیچ یک از دو آیکون شما دارای سایز برابر نباشند. 103 | 104 | برای مثال ساختار زیر باعث ایجاد خطا در برنامه میشود. 105 | 106 | 107 | .. code-block:: python 108 | :linenos: 109 | :emphasize-lines: 4,9 110 | 111 | 112 | { 113 | "src": "/static/pwa/icons/144x144.png", 114 | "type": "image/png", 115 | "sizes": "144x144" 116 | }, 117 | { 118 | "src": "/static/pwa/icons/144x144.ico", 119 | "type": "image/x-icon", 120 | "sizes": "144x144" 121 | }, 122 | 123 | 124 | .. note:: 125 | این بدین معنی است که لزوما تغییر فرمت موجب جلوگیری از خطا نمی‌شود. 126 | 127 | .. note:: 128 | همچنین ما توصیه نمی‌کنیم که ``PWA_APP`` و ``PWA_SW`` را دوباره تعریف کنید مگر آنکه بدانید واقعا چه می‌کنید. 129 | 130 | 131 | شما میتوانید آیکون‌های خود را در سایزهای مختلف با فرمت‌های دلخواه خود به صورت فوق تعریف کنید اما ما توصیه میکنیم از فرمت .ico استفاده کنید. 132 | 133 | 134 | تغییر ``manifest.json`` 135 | ########################## 136 | 137 | برای این کار ``PWA_CONFIG`` را به ``settings.py`` اضافه کنید. 138 | 139 | .. code-block:: python 140 | :linenos: 141 | 142 | # 143 | 144 | PWA_CONFIG = { 145 | "name": "My Costum Name", 146 | "short_name": "MCN", 147 | "theme_color": "#fff", 148 | "background_color": "#f0f0f0", 149 | "display": "standalone", 150 | "orientation": "portrait", 151 | "scope": "/", 152 | "start_url": "/", 153 | "icons": [ 154 | { 155 | "src": "/static/pwa/icons/72x72.png", 156 | "type": "image/png", 157 | "sizes": "72x72" 158 | }, 159 | { 160 | "src": "/static/pwa/icons/96x96.png", 161 | "type": "image/png", 162 | "sizes": "96x96" 163 | }, 164 | { 165 | "src": "/static/pwa/icons/128x128.png", 166 | "type": "image/png", 167 | "sizes": "128x128" 168 | }, 169 | { 170 | "src": "/static/pwa/icons/144x144.png", 171 | "type": "image/png", 172 | "sizes": "144x144" 173 | }, 174 | { 175 | "src": "/static/pwa/icons/152x152.png", 176 | "type": "image/png", 177 | "sizes": "152x152" 178 | }, 179 | { 180 | "src": "/static/pwa/icons/192x192.png", 181 | "type": "image/png", 182 | "sizes": "192x192" 183 | }, 184 | { 185 | "src": "/static/pwa/icons/384x384.png", 186 | "type": "image/png", 187 | "sizes": "384x384" 188 | }, 189 | { 190 | "src": "/static/pwa/icons/512x512.png", 191 | "type": "image/png", 192 | "sizes": "512x512" 193 | } 194 | ], 195 | "lang": "en", 196 | "dir": "ltr", 197 | "description": "Progressive Web app powerd by Django", 198 | "version": "1.", 199 | "manifest_version": "1.0", 200 | "permissions": [ 201 | "notifications", 202 | "webRequest" 203 | ], 204 | "author": "PWA-django" 205 | } 206 | 207 | 208 | 209 | مقادیر قابل قبول ``manifest.json`` 210 | ______________________________________ 211 | ما در اینجا برخی از مواردی که یک PWA میتواند در فایل ``manifest.json`` خود داشته باشد را به صورت پیشفرض برای اپ خود تعریف کرده‌ایم 212 | و در ادامه به تعریف مقادیری که میتوانید برای آن‌ها لحاظ کنید می‌پردازیم. 213 | 214 | 215 | .. code-block:: javascript 216 | :linenos: 217 | 218 | { 219 | "name": 'The name of application', 220 | "short_name": "Short name; Can be same with name", 221 | "theme_color": "The hex color for app theme", 222 | "background_color": "The hex color for app background color", 223 | "display": "fullscreen [OR] standalone [OR] minimal-ui [OR] browser", 224 | "orientation": "any [OR] natural [OR] landscape [OR] landscape-primary [OR] landscape-secondary [OR] portrait [OR] portrait-primary [OR] portrait-secondary", 225 | "scope": "/app/ [OR] https://example.com/ [OR] https://example.com/subdirectory/", 226 | "start_url": "/ [OR] https://example.com [OR] ../startpoint.html", 227 | "icons": "the list of dictionery that contains **src** and **sizes** and **type**" 228 | "lang": "langueage code like fa [OR] en [OR] tu [OR] fn [OR] ge [OR] ...", 229 | "dir": "rtl [OR] ltr [OR] auto", 230 | "description": "Description pf your app", 231 | "version": "app version", 232 | "manifest_version": "manifest.json file vertion if change on updating app", 233 | "permissions": `list here `_ 234 | "author": "author name or title of app" 235 | 236 | } 237 | 238 | 239 | 240 | 241 | .. note:: 242 | برای اطلاعات بیشتر می‌توانید به برخی مستندات آن که در لیست زیر فراهم کردیم بپردازید: 243 | 244 | * `developer.mozilla.org `__ 245 | * `developer.chrome.com `__ 246 | * `web.dev `__ 247 | 248 | 249 | 250 | تغییر ``ServiceWorker.js`` 251 | ############################# 252 | 253 | برای این کار ``PWA_SW`` را در ``settings.py`` به یکی از دو روش زیر تعریف کنید. 254 | 255 | 256 | .. code-block:: python 257 | :linenos: 258 | 259 | PWA_SW = """// js code here """ 260 | 261 | 262 | .. code-block:: python 263 | :linenos: 264 | 265 | SW = open('/path/to/ServiceWorker.js', "r") 266 | PWA_SW = SW.read() 267 | SW.close() 268 | 269 | 270 | .. note:: 271 | برای اطلاعات بیشتر نسبت به نحوه‌ی کارکرد ``ServiceWorker.js`` می‌توانید از منابع زیر استفاده کنید. 272 | 273 | * `developer.mozilla.org `__ 274 | * `developers.google.com `__ 275 | * `docs.microsoft.com `__ 276 | 277 | 278 | 279 | 280 | 281 | تغییر ``app.js`` 282 | ######################## 283 | 284 | برای این کار کافیست متغییر ``PWA_APP`` را در فایل ``settings.py`` تعریف کنید. 285 | 286 | 287 | .. code-block:: python 288 | :linenos: 289 | 290 | PWA_SW = """// js code here """ 291 | 292 | 293 | .. code-block:: python 294 | :linenos: 295 | 296 | APP = open('/path/to/app.js', "r") 297 | PWA_SW = APP.read() 298 | APP.close() 299 | 300 | 301 | 302 | 303 | .. note:: 304 | همچنان توصیه نمی‌کنیم که ``PWA_APP`` و ``PWA_SW`` را دوباره تعریف کنید مگر آن که بدانید واقعا چه می‌کنید. 305 | 306 | 307 | 308 | تغییر محتوای صفحه‌ی آفلاین 309 | ################################ 310 | ما از ساختاری مشابه ساختار جنگو برای تغییر صفحه‌ی آفلاین استفاده می‌کنیم. 311 | به طوری که برای تغییر آن می‌بایست ابتدا در مسیر ``/pwa/`` یک فایل با نام ``offline.html`` ایجاد کنید و 312 | سپس آن را در مسیر ``pwa/pwa_offline.html`` اکستند کنید. 313 | 314 | .. code-block:: html 315 | :linenos: 316 | 317 | 318 | 319 | {% extend 'pwa/pwa_offline.html' %} 320 | 321 | {% block title %} title {% endblock title %} 322 | {% block main %} something {% endblock main %} 323 | 324 | 325 | .. note:: 326 | ما به طور پیش‌فرض از `بوت‌استرپ `_ در قسمت آفلاین سایت استفاده می‌کنیم با این حال 327 | میتوانید به صورت زیر فایل‌های css و js خود را در آن بارگذاری کنید. 328 | 329 | .. code-block:: html 330 | :linenos: 331 | 332 | {% block extrastyles %} {% endblock extrastyles %} 333 | 334 | {% block extrascripts %} {% endblock extrascripts %} 335 | 336 | .. warning:: 337 | توجه داشته باشید با اعمال قطعه کد بالا دیگر بوت استرپ در صفحه اعمال نخواهد شد. 338 | 339 | درضمن هرگونه تغییر در فایل آفلاین باید با تغییر کش در ``ServiceWorker.js`` همراه باشد. 340 | بنابراین در صورتی که فایل آفلاین را تغییر دادید نیاز است که ``PWA_SW`` را نیز تعریف کنید هرچند 341 | ممکن است بدون این کار هم برنامه کار بکند ولی بهتر است که ``ServiceWorker.js`` باز نویسی شود. -------------------------------------------------------------------------------- /docs/source/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/docs/source/error.png -------------------------------------------------------------------------------- /docs/source/errors.rst: -------------------------------------------------------------------------------- 1 | .. _errors: 2 | 3 | 4 | خطا و ارورها 5 | ==================== 6 | 7 | 8 | در صورتی که با خطای زیر مواجه شدید آیکون‌های خود را به صورت صحیح آدرس دهی کنید. 9 | همچنین مطمئن شوید هیچ یک از دو آیکون شما دارای سایز برابر نمی‌باشند حتی اگر فرمت متفاوتی دارند. 10 | 11 | 12 | .. image:: error.png 13 | :alt: error.png 14 | 15 | 16 | 17 | عدم نمایش دکمه نصب 18 | ################### 19 | 20 | 21 | در صورتی که دکمه نصب نمایش داده نشد، مطمئن شوید که سرور بر روی لوکال هاست و یا HTTPS اجرا میشود. 22 | اجرای سرور بر روی شبکه NAT نیازمند دارا بودن گواهی SSL معتبر است. 23 | این موضوع به django-simple-pwa مربوط نیست و به ساختار PWA مربوط می‌شود. -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | 2 | django-simple-pwa 3 | ====================================== 4 | 5 | 6 | این اپ یک راهکار سریع برای ایجاد و کنترل اپ PWA برای وب سایت شماست. 7 | 8 | لطفا ما را برای ترجمه‌ی مستندات به زبان محلی خود یاری کنید. 9 | 10 | فهرست محتوا 11 | ================== 12 | 13 | * :ref:`quickstart` 14 | * :ref:`customization` 15 | * :ref:`errors` 16 | 17 | 18 | .. toctree:: 19 | :maxdepth: 3 20 | :caption: لیست 21 | 22 | quickstart 23 | customization 24 | errors 25 | 26 | -------------------------------------------------------------------------------- /docs/source/quickstart.rst: -------------------------------------------------------------------------------- 1 | .. _quickstart: 2 | 3 | شروع سریع 4 | ==================== 5 | 6 | 7 | نصب 8 | ------- 9 | ابتدا DSP (django-simple-pwa) را با دستور زیر نصب کنید 10 | 11 | 12 | .. code-block:: bash 13 | :linenos: 14 | 15 | pip install django-simple-pwa 16 | 17 | 18 | حال DSP را به اپ‌های نصب شده اضافه کنید. 19 | 20 | .. code-block:: python 21 | :linenos: 22 | :emphasize-lines: 5 23 | 24 | # 25 | 26 | INSTALLED_APPS = [ 27 | #... 28 | 'pwa', 29 | #... 30 | ] 31 | 32 | تنظیمات 33 | -------- 34 | حال urlهای DSP را به ``urls.py`` که در کنار ``setting.py`` واقع است به شکل زیر اضافه کنید. 35 | 36 | 37 | .. code-block:: python 38 | :linenos: 39 | :emphasize-lines: 6 40 | 41 | # 42 | 43 | from django.urls import path, include 44 | urlpatterns = [ 45 | #... 46 | path('', include('pwa.urls')), 47 | #... 48 | ] 49 | 50 | 51 | مهم است که محل فایل‌های استاتیک را در تنظمیات خود معرفی کنید. 52 | 53 | .. code-block:: python 54 | :linenos: 55 | 56 | # 57 | 58 | from os.path import join 59 | 60 | STATIC_URL = '/static/' 61 | STATIC_ROOT = str(join(BASE_DIR, 'static')) 62 | STATICFILES_DIRS = ( str(join(BASE_DIR, 'static_files')), ) 63 | 64 | همچنین فرمان ``./manage.py collectstatic`` را اجرا کنید. 65 | 66 | 67 | 68 | اجرا در لوکال هاست 69 | --------------------- 70 | 71 | در صورتی که پروژه‌ را در لوکال هاست خود اجرا می‌کنید، مطمئن شوید که فایل‌های استاتیک به درستی لود شده‌اند. 72 | 73 | 74 | .. code-block:: python 75 | :linenos: 76 | :emphasize-lines: 5 77 | 78 | # 79 | from django.conf.urls.static import static 80 | from django.conf import settings 81 | 82 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 83 | 84 | 85 | 86 | 87 | لود کردن فایل‌های ضروری 88 | -------------------------- 89 | 90 | حال در فایل html اصلی خود که عموما به نام ``index.html`` شناخته می‌شود، متا دیتا‌های اپ را لود کنید. 91 | 92 | .. code-block:: html 93 | :linenos: 94 | :emphasize-lines: 3,7 95 | 96 | {% load pwa %} 97 | 98 | {% pwa_meta_data %} 99 | 100 | 101 | 102 | {% pwa_meta_script %} 103 | 104 | 105 | 106 | 107 | .. note:: 108 | همچین می‌توانید این تنظیمات را در تمام صفحات اعمال کنید تا از طریق همه آن‌ها pwa در دسترس باشد. 109 | 110 | 111 | 112 | اگر همه مراحل را به درستی انجام داده باشید میتوانید اکنون سایت خود را بر روی دستگاه خود به صورت pwa نصب کنید. 113 | 114 | 115 | -------------------------------------------------------------------------------- /pwa/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/__init__.py -------------------------------------------------------------------------------- /pwa/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from pwa.models import Permissions, ProgressiveWebApplication, Icons 3 | 4 | 5 | @admin.register(Icons) 6 | class IconAdmin(admin.ModelAdmin): 7 | list_display = ("name", "color_scheme", "purpose",) 8 | 9 | 10 | @admin.register(Permissions) 11 | class PermissionsAdmin(admin.ModelAdmin): 12 | list_display = ("name",) 13 | 14 | 15 | @admin.register(ProgressiveWebApplication) 16 | class PWAAdmin(admin.ModelAdmin): 17 | list_display = ("name", "scope", "start_url",) 18 | -------------------------------------------------------------------------------- /pwa/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class PwaConfig(AppConfig): 6 | default_auto_field = 'django.db.models.BigAutoField' 7 | name = 'pwa' 8 | verbose_name = _("PWA") 9 | -------------------------------------------------------------------------------- /pwa/defaults.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | def get_pwa_config(): 6 | DEFAULT_CONFIG = { 7 | "name": _("Progressive Web Application"), 8 | "short_name": _("PWA"), 9 | "theme_color": "#7820f5", 10 | "background_color": "#7820f5", 11 | "display": "standalone", 12 | "orientation": "portrait", 13 | "scope": "/", 14 | "start_url": "/", 15 | "icons": [ 16 | { 17 | "src": "/static/pwa/images/icons/72x72.png", 18 | "type": "image/png", 19 | "sizes": "72x72" 20 | }, 21 | { 22 | "src": "/static/pwa/images/icons/96x96.png", 23 | "type": "image/png", 24 | "sizes": "96x96" 25 | }, 26 | { 27 | "src": "/static/pwa/images/icons/128x128.png", 28 | "type": "image/png", 29 | "sizes": "128x128" 30 | }, 31 | { 32 | "src": "/static/pwa/images/icons/144x144.png", 33 | "type": "image/png", 34 | "sizes": "144x144" 35 | }, 36 | { 37 | "src": "/static/pwa/images/icons/152x152.png", 38 | "type": "image/png", 39 | "sizes": "152x152" 40 | }, 41 | { 42 | "src": "/static/pwa/images/icons/192x192.png", 43 | "type": "image/png", 44 | "sizes": "192x192" 45 | }, 46 | { 47 | "src": "/static/pwa/images/icons/384x384.png", 48 | "type": "image/png", 49 | "sizes": "384x384" 50 | }, 51 | { 52 | "src": "/static/pwa/images/icons/512x512.png", 53 | "type": "image/png", 54 | "sizes": "512x512" 55 | } 56 | ], 57 | "lang": "en", 58 | "dir": "ltr", 59 | "description": _("Progressive Web app powered by Django"), 60 | "version": "1.", 61 | "manifest_version": "1.0", 62 | "permissions": [ 63 | "notifications", 64 | "webRequest" 65 | ], 66 | "author": _("PWA-django"), 67 | } 68 | try: 69 | if settings.PWA_CONFIG and settings.PWA_CONFIG != {}: 70 | return settings.PWA_CONFIG 71 | except: 72 | return DEFAULT_CONFIG 73 | 74 | 75 | def get_service_worker(): 76 | SERVICE_WORKER = """ 77 | var CACHE_NAME = 'pwa-cache-v1'; 78 | var urlsToCache = [ 79 | '/', 80 | '/sw.js', 81 | '/app.js', 82 | '/manifest.json', 83 | '/offline', 84 | '/static/pwa/images/dino.gif', 85 | 86 | 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', 87 | 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js', 88 | 89 | ]; 90 | const self = this; 91 | 92 | // Install SW 93 | self.addEventListener('install', (event) =>{ 94 | event.waitUntil( 95 | caches.open(CACHE_NAME) 96 | .then((cache) => { 97 | console.log('%s'); 98 | return cache.addAll(urlsToCache); 99 | }) 100 | ) 101 | }); 102 | 103 | // Listen For requests 104 | self.addEventListener('fetch', (event) =>{ 105 | event.respondWith( 106 | caches.match(event.request) 107 | .then(() => { 108 | return fetch(event.request) 109 | .catch(() => caches.match('/offline')) 110 | }) 111 | ) 112 | }); 113 | 114 | // Activate 115 | self.addEventListener('activate', (event) =>{ 116 | const cacheWhitelist = []; 117 | cacheWhitelist.push(CACHE_NAME); 118 | event.waitUntil( 119 | caches.keys().then((cacheNames) => Promise.all( 120 | cacheNames.map((cacheName) => { 121 | if (!cacheWhitelist.includes(cacheName)) { 122 | return caches.delete(cacheName); 123 | } 124 | }) 125 | )) 126 | ) 127 | }); 128 | """ % ( 129 | _("Cache Opend."), 130 | 131 | ) 132 | try: 133 | if settings.PWA_SW and settings.PWA_SW != {}: 134 | return settings.PWA_SW 135 | except: 136 | return SERVICE_WORKER 137 | 138 | 139 | def get_app(): 140 | APP = """ 141 | if ("serviceWorker" in navigator) { 142 | window.addEventListener("load", () => { 143 | navigator.serviceWorker 144 | .register("/sw.js") 145 | .then(res => console.log("%s")) 146 | .catch(err => console.log("%s", err)); 147 | }); 148 | } else { 149 | console.log(`%s`); 150 | }; 151 | """ % ( 152 | _("Service worker registered!"), 153 | _("Your browser supports serviceWorker, but the service worker is not registered."), 154 | _("Your browser doesn't support serviceWorker, so you can't install the PWA."), 155 | ) 156 | try: 157 | if settings.PWA_APP and settings.PWA_APP != {}: 158 | return settings.PWA_APP 159 | except: 160 | return APP 161 | 162 | 163 | 164 | 165 | PERMISSION_LIST = """ 166 | activeTab 167 | background 168 | bookmarks 169 | browserSettings 170 | clipboardRead 171 | clipboardWrite 172 | contentSettings 173 | contextMenus 174 | cookies 175 | debugger 176 | declarativeNetRequestFeedback 177 | downloads 178 | downloads.open 179 | find 180 | geolocation 181 | history 182 | idle 183 | management 184 | nativeMessaging 185 | notifications 186 | pageCapture 187 | privacy 188 | scripting 189 | tabHide 190 | tabs 191 | topSites 192 | webNavigation 193 | webRequest 194 | webRequestBlocking 195 | webRequestFilterResponse 196 | webRequestFilterResponse.serviceWorkerScript""" -------------------------------------------------------------------------------- /pwa/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-07 18:59 2 | 3 | import django.contrib.sites.managers 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ('sites', '0002_alter_domain_unique'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Icons', 19 | fields=[ 20 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(max_length=50, verbose_name='Name')), 22 | ('icon', models.FileField(upload_to='pwa/icons/', verbose_name='Icon')), 23 | ('icon_type', models.CharField(choices=[('image/jpeg', 'JPEG/JPG'), ('image/png', 'PNG'), ('image/svg+xml', 'SVG'), ('image/webp', 'WEBP'), ('image/avif', 'AVIF'), ('image/vnd.microsoft.icon', 'ICO')], max_length=25, verbose_name='Icon Type')), 24 | ('color_scheme', models.CharField(choices=[('Light', 'Light Mode'), ('Dark', 'Dark Mode')], default='Light', max_length=5, verbose_name='Color Scheme')), 25 | ('sizes', models.CharField(default='any', max_length=250, verbose_name='Sizes')), 26 | ('purpose', models.CharField(choices=[('monochrome', 'monochrome'), ('maskable', 'maskable'), ('any', 'any')], default='any', max_length=10, verbose_name='Purpose')), 27 | ], 28 | options={ 29 | 'verbose_name': 'Icon', 30 | 'verbose_name_plural': 'Icons', 31 | }, 32 | ), 33 | migrations.CreateModel( 34 | name='Permissions', 35 | fields=[ 36 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 37 | ('name', models.CharField(max_length=150, verbose_name='Name')), 38 | ('permistion', models.CharField(max_length=150, verbose_name='Permistion Code')), 39 | ], 40 | options={ 41 | 'verbose_name': 'Permission', 42 | 'verbose_name_plural': 'Permissions', 43 | }, 44 | ), 45 | migrations.CreateModel( 46 | name='ProgressiveWebApplication', 47 | fields=[ 48 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 49 | ('name', models.CharField(max_length=46, verbose_name='Name')), 50 | ('short_name', models.CharField(max_length=13, verbose_name='Short Name')), 51 | ('theme_color', models.CharField(max_length=9, verbose_name='Theme Color')), 52 | ('background_color', models.CharField(max_length=9, verbose_name='Background Color')), 53 | ('display', models.CharField(choices=[('fullscreen', 'fullscreen'), ('standalone', 'standalone'), ('minimal-ui', 'minimal-ui'), ('browser', 'browser')], max_length=10, verbose_name='Display')), 54 | ('orientation', models.CharField(choices=[('any', 'any'), ('natural', 'natural'), ('landscape', 'landscape'), ('landscape-primary', 'landscape-primary'), ('landscape-secondary', 'landscape-secondary'), ('portrait', 'portrait'), ('portrait-primary', 'portrait-primary'), ('portrait-secondary', 'portrait-secondary')], max_length=19, verbose_name='Orientation')), 55 | ('scope', models.SlugField(max_length=100, verbose_name='Scop')), 56 | ('start_url', models.SlugField(max_length=450, verbose_name='Start URL')), 57 | ('direction', models.CharField(choices=[('rtl', 'Right to Left'), ('ltr', 'Left to Right')], max_length=3, verbose_name='Direction')), 58 | ('lang', models.CharField(choices=[('af', 'Afrikaans'), ('ar', 'Arabic'), ('ar-dz', 'Algerian Arabic'), ('ast', 'Asturian'), ('az', 'Azerbaijani'), ('bg', 'Bulgarian'), ('be', 'Belarusian'), ('bn', 'Bengali'), ('br', 'Breton'), ('bs', 'Bosnian'), ('ca', 'Catalan'), ('ckb', 'Central Kurdish (Sorani)'), ('cs', 'Czech'), ('cy', 'Welsh'), ('da', 'Danish'), ('de', 'German'), ('dsb', 'Lower Sorbian'), ('el', 'Greek'), ('en', 'English'), ('en-au', 'Australian English'), ('en-gb', 'British English'), ('eo', 'Esperanto'), ('es', 'Spanish'), ('es-ar', 'Argentinian Spanish'), ('es-co', 'Colombian Spanish'), ('es-mx', 'Mexican Spanish'), ('es-ni', 'Nicaraguan Spanish'), ('es-ve', 'Venezuelan Spanish'), ('et', 'Estonian'), ('eu', 'Basque'), ('fa', 'Persian'), ('fi', 'Finnish'), ('fr', 'French'), ('fy', 'Frisian'), ('ga', 'Irish'), ('gd', 'Scottish Gaelic'), ('gl', 'Galician'), ('he', 'Hebrew'), ('hi', 'Hindi'), ('hr', 'Croatian'), ('hsb', 'Upper Sorbian'), ('hu', 'Hungarian'), ('hy', 'Armenian'), ('ia', 'Interlingua'), ('id', 'Indonesian'), ('ig', 'Igbo'), ('io', 'Ido'), ('is', 'Icelandic'), ('it', 'Italian'), ('ja', 'Japanese'), ('ka', 'Georgian'), ('kab', 'Kabyle'), ('kk', 'Kazakh'), ('km', 'Khmer'), ('kn', 'Kannada'), ('ko', 'Korean'), ('ky', 'Kyrgyz'), ('lb', 'Luxembourgish'), ('lt', 'Lithuanian'), ('lv', 'Latvian'), ('mk', 'Macedonian'), ('ml', 'Malayalam'), ('mn', 'Mongolian'), ('mr', 'Marathi'), ('ms', 'Malay'), ('my', 'Burmese'), ('nb', 'Norwegian Bokmål'), ('ne', 'Nepali'), ('nl', 'Dutch'), ('nn', 'Norwegian Nynorsk'), ('os', 'Ossetic'), ('pa', 'Punjabi'), ('pl', 'Polish'), ('pt', 'Portuguese'), ('pt-br', 'Brazilian Portuguese'), ('ro', 'Romanian'), ('ru', 'Russian'), ('sk', 'Slovak'), ('sl', 'Slovenian'), ('sq', 'Albanian'), ('sr', 'Serbian'), ('sr-latn', 'Serbian Latin'), ('sv', 'Swedish'), ('sw', 'Swahili'), ('ta', 'Tamil'), ('te', 'Telugu'), ('tg', 'Tajik'), ('th', 'Thai'), ('tk', 'Turkmen'), ('tr', 'Turkish'), ('tt', 'Tatar'), ('udm', 'Udmurt'), ('uk', 'Ukrainian'), ('ur', 'Urdu'), ('uz', 'Uzbek'), ('vi', 'Vietnamese'), ('zh-hans', 'Simplified Chinese'), ('zh-hant', 'Traditional Chinese')], max_length=50, verbose_name='Language')), 59 | ('description', models.TextField(verbose_name='Description')), 60 | ('version', models.CharField(default='1.0.0', max_length=15, verbose_name='Version')), 61 | ('manifest_version', models.PositiveSmallIntegerField(choices=[(1, 'Vertion 1'), (2, 'Vertion 2'), (3, 'Vertion 3')], default=3, verbose_name='Manifest Version')), 62 | ('author', models.CharField(max_length=150, verbose_name='Author')), 63 | ('icons', models.ManyToManyField(related_name='pwa_icons', to='pwa.icons', verbose_name='Icons')), 64 | ('permissions', models.ManyToManyField(related_name='pwa_permissions', to='pwa.permissions', verbose_name='Permissions')), 65 | ('site', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='pwa_site', to='sites.site', verbose_name='Site')), 66 | ], 67 | options={ 68 | 'verbose_name': 'PWA', 69 | 'verbose_name_plural': 'PWAs', 70 | }, 71 | managers=[ 72 | ('on_site', django.contrib.sites.managers.CurrentSiteManager()), 73 | ], 74 | ), 75 | migrations.AddConstraint( 76 | model_name='progressivewebapplication', 77 | constraint=models.UniqueConstraint(fields=('site',), name='pwa_progressivewebapplication_unique'), 78 | ), 79 | ] 80 | -------------------------------------------------------------------------------- /pwa/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/migrations/__init__.py -------------------------------------------------------------------------------- /pwa/models.py: -------------------------------------------------------------------------------- 1 | 2 | from django.db import models 3 | from django.utils.translation import gettext_lazy as _ 4 | from django.contrib.sites.models import Site 5 | from django.contrib.sites.managers import CurrentSiteManager 6 | from django.conf.global_settings import LANGUAGES 7 | 8 | 9 | class IconTypeChoices(models.TextChoices): 10 | JPEG = "image/jpeg", _("JPEG/JPG") 11 | PNG = "image/png", _("PNG") 12 | SVG = "image/svg+xml", _("SVG") 13 | WEBP = "image/webp", _("WEBP") 14 | AVIF = "image/avif", _("AVIF") 15 | ICO = "image/vnd.microsoft.icon", _("ICO") 16 | 17 | 18 | class IconColorSchemeChoices(models.TextChoices): 19 | LIGHT = "Light", _("Light Mode") 20 | DARK = "Dark", _("Dark Mode") 21 | 22 | 23 | class IconPurposeChoices(models.TextChoices): 24 | MONOCHROME = "monochrome", _("monochrome") 25 | MASKABLE = "maskable", _("maskable") 26 | ANY = "any", _("any") 27 | 28 | 29 | class Icons(models.Model): 30 | name = models.CharField(max_length=50, verbose_name=_("Name")) 31 | icon = models.FileField(verbose_name=_("Icon"), upload_to="pwa/icons/") 32 | icon_type = models.CharField( 33 | max_length=25, choices=IconTypeChoices.choices, verbose_name=_("Icon Type")) 34 | color_scheme = models.CharField( 35 | max_length=5, choices=IconColorSchemeChoices.choices, default="Light", 36 | verbose_name=_("Color Scheme")) 37 | sizes = models.CharField( 38 | max_length=250, default="any", verbose_name=_("Sizes")) 39 | purpose = models.CharField( 40 | max_length=10, choices=IconPurposeChoices.choices, default="any", verbose_name=_("Purpose")) 41 | 42 | class Meta: 43 | verbose_name = _("Icon") 44 | verbose_name_plural = _("Icons") 45 | 46 | def __str__(self) -> str: 47 | return f"{self.name} - {self.sizes}" 48 | 49 | 50 | class DisplayChoices(models.TextChoices): 51 | FULLSCREEN = "fullscreen", _("fullscreen") 52 | STANDALONE = "standalone", _("standalone") 53 | MINIMAL_UI = "minimal-ui", _("minimal-ui") 54 | BROWSER = "browser", _("browser") 55 | 56 | 57 | class OrientationChoices(models.TextChoices): 58 | ANY = "any", _("any") 59 | MATURAL = "natural", _("natural") 60 | LANDSCAPE = "landscape", _("landscape") 61 | LANDSCAPE_PRIMARY = "landscape-primary", _("landscape-primary") 62 | LANDSCAPE_SECONDARY = "landscape-secondary", _("landscape-secondary") 63 | PORTRAIT = "portrait", _("portrait") 64 | PORTRAIT_PRIMARY = "portrait-primary", _("portrait-primary") 65 | PORTRAIT_SECONDARY = "portrait-secondary", _("portrait-secondary") 66 | 67 | 68 | class DirectionChoices(models.TextChoices): 69 | RTL = "rtl", _("Right to Left") 70 | LTR = "ltr", _("Left to Right") 71 | 72 | 73 | class ManifestVersion(models.IntegerChoices): 74 | V1 = 1, _("Version 1") 75 | V2 = 2, _("Version 2") 76 | V3 = 3, _("Version 3") 77 | 78 | 79 | class Permissions(models.Model): 80 | name = models.CharField(max_length=150, verbose_name=_("Name")) 81 | permistion = models.CharField( 82 | max_length=150, verbose_name=_("Permistion Code")) 83 | 84 | class Meta: 85 | verbose_name = _("Permission") 86 | verbose_name_plural = _("Permissions") 87 | 88 | def __str__(self) -> str: 89 | return f"{self.name}" 90 | 91 | 92 | class ProgressiveWebApplication(models.Model): 93 | site = models.OneToOneField( 94 | Site, on_delete=models.CASCADE, verbose_name=_("Site"), related_name="pwa_site") 95 | name = models.CharField(max_length=46, verbose_name=_("Name")) 96 | short_name = models.CharField(max_length=13, verbose_name=_("Short Name")) 97 | theme_color = models.CharField(max_length=9, verbose_name=_("Theme Color")) 98 | background_color = models.CharField( 99 | max_length=9, verbose_name=_("Background Color")) 100 | display = models.CharField( 101 | max_length=10, choices=DisplayChoices.choices, verbose_name=_("Display")) 102 | orientation = models.CharField( 103 | max_length=19, choices=OrientationChoices.choices, verbose_name=_("Orientation")) 104 | scope = models.SlugField(max_length=100, verbose_name=_("Scop")) 105 | start_url = models.SlugField(max_length=450, verbose_name=_("Start URL")) 106 | icons = models.ManyToManyField( 107 | Icons, verbose_name=_("Icons"), related_name="pwa_icons") 108 | direction = models.CharField( 109 | max_length=3, choices=DirectionChoices.choices, verbose_name=_("Direction")) 110 | lang = models.CharField( 111 | max_length=50, choices=LANGUAGES, verbose_name=_("Language")) 112 | description = models.TextField(verbose_name=_("Description")) 113 | version = models.CharField( 114 | max_length=15, default="1.0.0", verbose_name=_("Version")) 115 | manifest_version = models.PositiveSmallIntegerField( 116 | default=3, choices=ManifestVersion.choices, verbose_name=_("Manifest Version")) 117 | permissions = models.ManyToManyField( 118 | Permissions, verbose_name=_("Permissions"), related_name="pwa_permissions") 119 | author = models.CharField(max_length=150, verbose_name=_("Author")) 120 | 121 | on_site = CurrentSiteManager() 122 | 123 | class Meta: 124 | verbose_name = _("PWA") 125 | verbose_name_plural = _("PWAs") 126 | constraints = [ 127 | models.UniqueConstraint( 128 | fields=['site', ], name="%(app_label)s_%(class)s_unique") 129 | ] 130 | 131 | def __str__(self) -> str: 132 | return f"{self.name} - {self.site.name}" 133 | -------------------------------------------------------------------------------- /pwa/static/pwa/images/dino.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/static/pwa/images/dino.gif -------------------------------------------------------------------------------- /pwa/static/pwa/images/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/static/pwa/images/icons/128x128.png -------------------------------------------------------------------------------- /pwa/static/pwa/images/icons/144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/static/pwa/images/icons/144x144.png -------------------------------------------------------------------------------- /pwa/static/pwa/images/icons/152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/static/pwa/images/icons/152x152.png -------------------------------------------------------------------------------- /pwa/static/pwa/images/icons/192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/static/pwa/images/icons/192x192.png -------------------------------------------------------------------------------- /pwa/static/pwa/images/icons/384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/static/pwa/images/icons/384x384.png -------------------------------------------------------------------------------- /pwa/static/pwa/images/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/static/pwa/images/icons/512x512.png -------------------------------------------------------------------------------- /pwa/static/pwa/images/icons/72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/static/pwa/images/icons/72x72.png -------------------------------------------------------------------------------- /pwa/static/pwa/images/icons/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/static/pwa/images/icons/96x96.png -------------------------------------------------------------------------------- /pwa/static/pwa/images/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/static/pwa/images/icons/favicon.ico -------------------------------------------------------------------------------- /pwa/templates/pwa/meta_script.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /pwa/templates/pwa/metadata.html: -------------------------------------------------------------------------------- 1 | 2 | {% load static %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% if THEME_COLOR %} 12 | 13 | 14 | 15 | {% endif %} 16 | {% if ICONS %} 17 | {% for icon in ICONS %} 18 | 19 | 20 | 21 | 22 | {% endfor %} 23 | {% endif %} 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /pwa/templates/pwa/pwa_offline.html: -------------------------------------------------------------------------------- 1 | {% load static pwa i18n %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% pwa_meta_data %} 10 | 11 | 12 | {% block title %}{% translate "Offline" %}{% endblock title %} 13 | 14 | 15 | {% block extrastyles %} 16 | 17 | {% endblock extrastyles %} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | {% block main %} 28 |
29 |

{% translate "Offline" %}

30 |

{% translate "You are offline, please check your internet connections." %}

31 | 32 |

33 | {% endblock main %} 34 | 35 | {% block extrascripts %} 36 | 37 | 38 | {% endblock extrascripts %} 39 | 40 | {% pwa_meta_script %} 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /pwa/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bambier/django-simple-pwa/3287e52d39f39d97b71c9c6c0175a6268b8b1287/pwa/templatetags/__init__.py -------------------------------------------------------------------------------- /pwa/templatetags/pwa.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | from pwa.defaults import get_pwa_config 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.inclusion_tag('pwa/metadata.html') 9 | def pwa_meta_data(): 10 | PWA_CONFIG = get_pwa_config() 11 | ICONS = PWA_CONFIG.get('icons', None) 12 | THEME_COLOR = PWA_CONFIG.get('theme_color', None) 13 | return {'ICONS':ICONS, 'THEME_COLOR':THEME_COLOR} 14 | 15 | 16 | @register.inclusion_tag('pwa/meta_script.html') 17 | def pwa_meta_script(): 18 | return {} 19 | -------------------------------------------------------------------------------- /pwa/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from pwa.views import manifest_json, sw_js, app_js, offline 3 | 4 | app_name = 'pwa' 5 | 6 | urlpatterns = [ 7 | path('sw.js', sw_js, name="sw.js"), 8 | path('manifest.json', manifest_json, name="manifest.json"), 9 | path('app.js', app_js, name="app.js"), 10 | 11 | path('offline', offline, name="offline"), 12 | 13 | ] 14 | -------------------------------------------------------------------------------- /pwa/views.py: -------------------------------------------------------------------------------- 1 | from django.http.response import HttpResponse, JsonResponse 2 | from django.shortcuts import render 3 | 4 | from pwa.defaults import get_app, get_pwa_config, get_service_worker 5 | 6 | # Create your views here. 7 | 8 | 9 | def manifest_json(request): 10 | return JsonResponse(get_pwa_config()) 11 | 12 | 13 | def sw_js(request): 14 | return HttpResponse(get_service_worker(), content_type='application/javascript') 15 | 16 | 17 | def app_js(request): 18 | return HttpResponse(get_app(), content_type='application/javascript') 19 | 20 | 21 | def offline(request): 22 | try: 23 | return render(request, 'pwa/offline.html') 24 | except: 25 | return render(request, 'pwa/pwa_offline.html') 26 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | 4 | with open("README.rst", "r+", encoding="UTF-8") as file: 5 | readme = file.read() 6 | 7 | 8 | setup( 9 | name='django-simple-pwa', 10 | description="A simple Django app to develope and deploy a Progressive Web Application (PWA).", 11 | long_description=readme, 12 | long_description_content_type='text/markdown', 13 | version='3.0.0', 14 | platforms='ALL', 15 | license='MIT', 16 | extras_require={ 17 | 'django': ['django>=4.2'] 18 | }, 19 | author='Nima Esmaeili', 20 | author_email='nimpel2@proton.me', 21 | maintainer='Nima Esmaeili', 22 | maintainer_email='nimpel2@proton.me', 23 | package_dir={'': ''}, 24 | include_package_data=True, 25 | keywords='django pwa django-simple-pwa', 26 | project_urls={ 27 | "GitHub":'https://github.com/bambier/django-simple-pwa/', 28 | "Documents": 'https://github.com/bambier/django-simple-pwa', 29 | } 30 | options=[ 31 | "include_package_data = true", 32 | "packages = find:", 33 | "python_requires = >=3.8", 34 | "install_requires =", 35 | "Django >= 3.7", 36 | ] 37 | classifiers=[ 38 | "Environment :: Web Environment", 39 | "Framework :: Django", 40 | "Framework :: Django :: 4.2", 41 | "Intended Audience :: Developers", 42 | "License :: OSI Approved :: MIT License", 43 | "Operating System :: OS Independent", 44 | "Programming Language :: Python", 45 | "Programming Language :: Python :: 3", 46 | "Programming Language :: Python :: 3 :: Only", 47 | "Programming Language :: Python :: 3.11", 48 | "Topic :: Internet :: WWW/HTTP", 49 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content ", 50 | ], 51 | ) 52 | --------------------------------------------------------------------------------