├── .github └── workflows │ └── django.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── accounts ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── templates │ ├── profile.html │ └── registration │ │ ├── change_password.html │ │ ├── login.html │ │ └── signup.html ├── tests │ ├── __init__.py │ ├── test_forms.py │ ├── test_models.py │ └── test_views.py ├── urls.py └── views.py ├── codehub ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── locale └── fa │ └── LC_MESSAGES │ ├── django.mo │ └── django.po ├── main ├── __init__.py ├── admin.py ├── apps.py ├── constants.py ├── context_processor.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── serializers.py ├── static │ ├── css │ │ └── style.css │ ├── fonts │ │ ├── ubuntu │ │ │ ├── UbuntuMono-Bold.ttf │ │ │ ├── UbuntuMono-BoldItalic.ttf │ │ │ ├── UbuntuMono-Italic.ttf │ │ │ └── UbuntuMono-Regular.ttf │ │ └── vazir │ │ │ ├── Farsi-Digits │ │ │ ├── Vazir-Black-FD.eot │ │ │ ├── Vazir-Black-FD.ttf │ │ │ ├── Vazir-Black-FD.woff │ │ │ ├── Vazir-Black-FD.woff2 │ │ │ ├── Vazir-Bold-FD.eot │ │ │ ├── Vazir-Bold-FD.ttf │ │ │ ├── Vazir-Bold-FD.woff │ │ │ ├── Vazir-Bold-FD.woff2 │ │ │ ├── Vazir-Light-FD.eot │ │ │ ├── Vazir-Light-FD.ttf │ │ │ ├── Vazir-Light-FD.woff │ │ │ ├── Vazir-Light-FD.woff2 │ │ │ ├── Vazir-Medium-FD.eot │ │ │ ├── Vazir-Medium-FD.ttf │ │ │ ├── Vazir-Medium-FD.woff │ │ │ ├── Vazir-Medium-FD.woff2 │ │ │ ├── Vazir-Regular-FD.eot │ │ │ ├── Vazir-Regular-FD.ttf │ │ │ ├── Vazir-Regular-FD.woff │ │ │ ├── Vazir-Regular-FD.woff2 │ │ │ ├── Vazir-Thin-FD.eot │ │ │ ├── Vazir-Thin-FD.ttf │ │ │ ├── Vazir-Thin-FD.woff │ │ │ └── Vazir-Thin-FD.woff2 │ │ │ ├── Vazir-Black.eot │ │ │ ├── Vazir-Black.ttf │ │ │ ├── Vazir-Black.woff │ │ │ ├── Vazir-Black.woff2 │ │ │ ├── Vazir-Bold.eot │ │ │ ├── Vazir-Bold.ttf │ │ │ ├── Vazir-Bold.woff │ │ │ ├── Vazir-Bold.woff2 │ │ │ ├── Vazir-Light.eot │ │ │ ├── Vazir-Light.ttf │ │ │ ├── Vazir-Light.woff │ │ │ ├── Vazir-Light.woff2 │ │ │ ├── Vazir-Medium.eot │ │ │ ├── Vazir-Medium.ttf │ │ │ ├── Vazir-Medium.woff │ │ │ ├── Vazir-Medium.woff2 │ │ │ ├── Vazir-Regular.eot │ │ │ ├── Vazir-Regular.ttf │ │ │ ├── Vazir-Regular.woff │ │ │ ├── Vazir-Regular.woff2 │ │ │ ├── Vazir-Thin.eot │ │ │ ├── Vazir-Thin.ttf │ │ │ ├── Vazir-Thin.woff │ │ │ ├── Vazir-Thin.woff2 │ │ │ ├── Vazir-Variable.eot │ │ │ ├── Vazir-Variable.ttf │ │ │ ├── Vazir-Variable.woff │ │ │ └── Vazir-Variable.woff2 │ └── img │ │ ├── development-banner.png │ │ ├── download-banner.png │ │ ├── fav.ico │ │ ├── figure.png │ │ ├── home-banner.png │ │ ├── icons │ │ └── material │ │ │ ├── arduino.svg │ │ │ ├── bash.svg │ │ │ ├── c.svg │ │ │ ├── cpp.svg │ │ │ ├── csharp.svg │ │ │ ├── css.svg │ │ │ ├── dart.svg │ │ │ ├── docker.svg │ │ │ ├── go.svg │ │ │ ├── html.svg │ │ │ ├── java.svg │ │ │ ├── js.svg │ │ │ ├── json.svg │ │ │ ├── lua.svg │ │ │ ├── md.svg │ │ │ ├── mysql.svg │ │ │ ├── php.svg │ │ │ ├── python.svg │ │ │ └── rb.svg │ │ ├── logo.png │ │ ├── offcanvas.png │ │ └── solid-banner.png ├── templates │ ├── 403.html │ ├── 404.html │ ├── development.html │ ├── download.html │ └── home.html ├── templatetags │ └── markdown_extras.py ├── urls.py ├── utils.py └── views.py ├── manage.py ├── requirements.txt ├── snippets ├── __init__.py ├── admin.py ├── apps.py ├── constants.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── serializers.py ├── templates │ ├── create_snippet.html │ └── snippet.html ├── tests.py ├── tests │ ├── __init__.py │ └── test_models.py ├── urls.py └── views.py ├── templates └── _base.html └── tickets ├── __init__.py ├── admin.py ├── apps.py ├── constants.py ├── forms.py ├── migrations ├── 0001_initial.py └── __init__.py ├── models.py ├── templates ├── create_ticket.html ├── ticket.html └── ticket_search_results.html ├── tests.py ├── tests ├── __init__.py └── test_models.py ├── urls.py └── views.py /.github/workflows/django.yml: -------------------------------------------------------------------------------- 1 | name: Django CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | max-parallel: 4 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Set up Python 3.10 19 | uses: actions/setup-python@v3 20 | with: 21 | python-version: 3.10.2 22 | - name: Install Dependencies 23 | run: | 24 | python -m pip install --upgrade pip 25 | pip install -r requirements.txt 26 | - name: Run Tests 27 | env: 28 | SECRET_KEY: ${{ secrets.SECRET_KEY }} 29 | ADMIN_PATH: ${{ secrets.ADMIN_PATH }} 30 | run: | 31 | echo $SECRET_KEY >> .env 32 | echo $ADMIN_PATH >> .env 33 | python manage.py test --pattern="test_*.py" 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ngo # 2 | *.log 3 | *.pot 4 | *.pyc 5 | __pycache__ 6 | db.sqlite3 7 | media 8 | 9 | # Backup files # 10 | *.bak 11 | 12 | # If you are using PyCharm # 13 | .idea/**/workspace.xml 14 | .idea/**/tasks.xml 15 | .idea/dictionaries 16 | .idea/**/dataSources/ 17 | .idea/**/dataSources.ids 18 | .idea/**/dataSources.xml 19 | .idea/**/dataSources.local.xml 20 | .idea/**/sqlDataSources.xml 21 | .idea/**/dynamic.xml 22 | .idea/**/uiDesigner.xml 23 | .idea/**/gradle.xml 24 | .idea/**/libraries 25 | *.iws /out/ 26 | 27 | # Python # 28 | *.py[cod] 29 | *$py.class 30 | 31 | # Distribution / packaging 32 | .Python build/ 33 | develop-eggs/ 34 | dist/ 35 | downloads/ 36 | eggs/ 37 | .eggs/ 38 | lib/ 39 | lib64/ 40 | parts/ 41 | sdist/ 42 | var/ 43 | wheels/ 44 | *.egg-info/ 45 | .installed.cfg 46 | *.egg 47 | *.manifest 48 | *.spec 49 | 50 | # Installer logs 51 | pip-log.txt 52 | pip-delete-this-directory.txt 53 | 54 | # Unit test / coverage reports 55 | htmlcov/ 56 | .tox/ 57 | .coverage 58 | .coverage.* 59 | .cache 60 | .pytest_cache/ 61 | nosetests.xml 62 | coverage.xml 63 | *.cover 64 | .hypothesis/ 65 | 66 | # Jupyter Notebook 67 | .ipynb_checkpoints 68 | 69 | # pyenv 70 | .python-version 71 | 72 | # celery 73 | celerybeat-schedule.* 74 | 75 | # SageMath parsed files 76 | *.sage.py 77 | 78 | # Environments 79 | .env 80 | .venv 81 | env/ 82 | venv/ 83 | ENV/ 84 | env.bak/ 85 | venv.bak/ 86 | 87 | # mkdocs documentation 88 | /site 89 | 90 | # mypy 91 | .mypy_cache/ 92 | 93 | # Sublime Text # 94 | *.tmlanguage.cache 95 | *.tmPreferences.cache 96 | *.stTheme.cache 97 | *.sublime-workspace 98 | *.sublime-project 99 | 100 | # sftp configuration file 101 | sftp-config.json 102 | 103 | # Package control specific files Package 104 | Control.last-run 105 | Control.ca-list 106 | Control.ca-bundle 107 | Control.system-ca-bundle 108 | GitHub.sublime-settings 109 | 110 | # Visual Studio Code # 111 | .vscode/* 112 | !.vscode/settings.json 113 | !.vscode/tasks.json 114 | !.vscode/launch.json 115 | !.vscode/extensions.json 116 | .history 117 | 118 | # Custom Ignores 119 | local_settings.py 120 | static/ 121 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Will be updated soon.. 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Sadra Yahyapour 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

CodeHub - کُد هاب

4 |
First Persian Pastebin Service
5 |

6 | 7 | Version 8 | 9 | 10 | 11 |


12 | 13 | Codehub is the first Persian pastebin service that is being developed as open source. Main technologies used in this project are [Django](https://github.com/django/django) and [Bootstrap](https://getbootstrap.com/). 14 | 15 | ### Contribution 16 | Any developer can contribute to Codehub and all your contributions are passionately welcome. Before you start contributing, you better follow the instruction and steps on [CONTRIBUTING.md](./CONTRIBUTING.md). 17 | 18 | ### ToDo 19 | - [ ] Observing PEP8 for Imports 20 | 21 | ### About @lnxpy 22 | I'm [Sadra](https://github.com/lnxpy). Python programmer and open source developer, interested in Machine Learning and [@codehub-ir](https://github.com/codehub-ir) creator. Follow me on socials. :) 23 | 24 |

25 | 26 | 27 | 28 | 29 |

30 | -------------------------------------------------------------------------------- /accounts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/accounts/__init__.py -------------------------------------------------------------------------------- /accounts/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth.admin import UserAdmin 3 | 4 | from .forms import UserChangeForm, UserCreationForm 5 | from .models import User 6 | 7 | 8 | class UserAdmin(UserAdmin): 9 | add_form = UserCreationForm 10 | form = UserChangeForm 11 | model = User 12 | list_display = ['username', 'email'] 13 | 14 | 15 | admin.site.register(User, UserAdmin) 16 | -------------------------------------------------------------------------------- /accounts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'accounts' 7 | -------------------------------------------------------------------------------- /accounts/forms.py: -------------------------------------------------------------------------------- 1 | from crispy_forms.helper import FormHelper 2 | from crispy_forms.layout import Submit 3 | from django.contrib.auth.forms import (PasswordChangeForm, UserChangeForm, 4 | UserCreationForm) 5 | from django.forms import HiddenInput 6 | from django.utils.translation import gettext as _ 7 | 8 | from .models import User 9 | 10 | 11 | class CustomUserCreationForm(UserCreationForm): 12 | 13 | def __init__(self, *args, **kwargs): 14 | super().__init__(*args, **kwargs) 15 | 16 | for visible in self.visible_fields(): 17 | visible.field.widget.attrs['class'] = 'crispy-form-item' 18 | visible.field.widget.attrs['spellcheck'] = 'false' 19 | 20 | class Meta: 21 | model = User 22 | fields = ('username', 'email', 'display_name') 23 | 24 | helper = FormHelper() 25 | helper.add_input(Submit('submit', _('Signup'), 26 | css_class='btn-primary crispy-form-item')) 27 | 28 | 29 | class CustomUserUpdateForm(UserChangeForm): 30 | 31 | def __init__(self, *args, **kwargs): 32 | super().__init__(*args, **kwargs) 33 | self.fields['password'].widget = HiddenInput() 34 | 35 | for visible in self.visible_fields(): 36 | visible.field.widget.attrs['class'] = 'crispy-form-item' 37 | visible.field.widget.attrs['spellcheck'] = 'false' 38 | 39 | class Meta: 40 | model = User 41 | fields = ('display_name', 42 | 'email', 43 | 'twitter', 44 | 'github', 45 | ) 46 | 47 | helper = FormHelper() 48 | helper.add_input(Submit('submit', _('Update'), 49 | css_class='btn-primary crispy-form-item')) 50 | 51 | 52 | class CustomPasswordChangeForm(PasswordChangeForm): 53 | 54 | def __init__(self, *args, **kwargs): 55 | super().__init__(*args, **kwargs) 56 | 57 | for visible in self.visible_fields(): 58 | visible.field.widget.attrs['class'] = 'crispy-form-item' 59 | visible.field.widget.attrs['spellcheck'] = 'false' 60 | 61 | class Meta: 62 | model = User 63 | 64 | helper = FormHelper() 65 | helper.add_input(Submit('submit', _('Update'), 66 | css_class='btn-primary crispy-form-item')) 67 | -------------------------------------------------------------------------------- /accounts/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.3 on 2022-03-25 01:30 2 | 3 | import django.contrib.auth.models 4 | import django.contrib.auth.validators 5 | from django.db import migrations, models 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('auth', '0012_alter_user_first_name_max_length'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='User', 20 | fields=[ 21 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('password', models.CharField(max_length=128, verbose_name='password')), 23 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 24 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 25 | ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), 26 | ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), 27 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 28 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 29 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 30 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 31 | ('display_name', models.CharField(max_length=50, verbose_name='نام قابل نمایش')), 32 | ('email', models.EmailField(max_length=254, verbose_name='آدرس ایمیل')), 33 | ('twitter', models.URLField(blank=True, verbose_name='آدرس توئیتر')), 34 | ('github', models.URLField(blank=True, verbose_name='آدرس گیت هاب')), 35 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), 36 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), 37 | ], 38 | options={ 39 | 'verbose_name': 'user', 40 | 'verbose_name_plural': 'users', 41 | 'abstract': False, 42 | }, 43 | managers=[ 44 | ('objects', django.contrib.auth.models.UserManager()), 45 | ], 46 | ), 47 | ] 48 | -------------------------------------------------------------------------------- /accounts/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/accounts/migrations/__init__.py -------------------------------------------------------------------------------- /accounts/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import AbstractUser 2 | from django.db import models 3 | from django.utils.translation import gettext as _ 4 | 5 | 6 | class User(AbstractUser): 7 | 8 | display_name = models.CharField( 9 | _('Display Name'), 10 | max_length=50, 11 | ) 12 | email = models.EmailField( 13 | _('Email Address'), 14 | ) 15 | twitter = models.URLField( 16 | _('Twitter Address'), 17 | blank=True, 18 | ) 19 | github = models.URLField( 20 | _('Github Address'), 21 | blank=True, 22 | ) 23 | 24 | def __str__(self): return self.username 25 | -------------------------------------------------------------------------------- /accounts/templates/profile.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load static %} 3 | {% load crispy_forms_tags %} 4 | 5 | {% block carousel_image %}{% static 'img/profile-banner.png' %}{% endblock %} 6 | {% block profile_active %}active{% endblock %} 7 | {% block title %}پروفایل شخصی{% endblock %} 8 | 9 | {% block content %} 10 | 35 | 36 |
37 |
38 | {% crispy form %} 39 |
40 |

تغییر رمز ورود

41 |

برای تغییر رمز کاربری، اینجا را کلیک کنید.

42 |
43 |
44 | {% if tickets.exists %} 45 |
تا امروز، تعداد {{tickets.count}} برچسب ایجاد کردید!

46 | {% for ticket in tickets %} 47 |
48 |
49 | 50 |
{{ticket.title}} 51 | {% if ticket.is_valid == "approved" %} 52 | منتشر شد! 53 | {% elif ticket.is_valid == "rejected" %} 54 | رد شد! 55 | {% else %} 56 | در حال بررسی.. 57 | {% endif %} 58 |
59 |

{{ticket.description|truncatewords:15}}

60 |

ثبت شده در {{ticket.created_on |date:'d-m-Y'}} 61 | {% for tag in ticket.tags.all %} 62 | {{tag.name}}# 64 | {% endfor %} 65 |

66 |
67 |
68 | {% endfor %} 69 | {% else %} 70 | 71 |

هنوز برچسبی ایجاد نکردید! :(

72 | {% endif %} 73 |
74 |
75 | 76 | {% if snippets.exists %} 77 |
تا امروز، تعداد {{snippets.count}} تکه‌کد ساختید!

78 | {% for snippet in snippets %} 79 |
80 |
81 | 82 |
{{snippet.title}}
83 |

{{snippet.description|truncatewords:15}}

84 |

ثبت شده در {{snippet.created_on |date:'d-m-Y'}} 85 |  {{snippet.lang}} 87 |

88 |
89 |
90 | {% endfor %} 91 | {% else %} 92 |

هنوز تکه‌کدی ایجاد نکردید! :(

93 | {% endif %} 94 | 95 |
96 |
97 | 98 | {% if comments.exists %} 99 |
تا امروز، تعداد {{comments.count}} نظر ثبت کردید!

100 | {% for comment in comments %} 101 |
102 |
103 | 104 |
#{{comment.ticket.title}}
105 |

{{comment.body|truncatewords:15}}

106 |

ثبت شده در {{comment.created_on |date:'d-m-Y'}} 107 |

108 |
109 |
110 | {% endfor %} 111 | {% else %} 112 |

هنوز نظری ثبت نکردید! :(

113 | {% endif %} 114 | 115 |
116 |
...
117 |
118 | 119 | 120 | 137 | {% endblock %} -------------------------------------------------------------------------------- /accounts/templates/registration/change_password.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block title %}تغییر رمز کاریری{% endblock %} 5 | 6 | {% block content %} 7 |

بروزرسانی رمز کاربری


8 | {% crispy form %} 9 | {% endblock %} -------------------------------------------------------------------------------- /accounts/templates/registration/login.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load static %} 3 | 4 | {% block meta_og_title %}ورود به حساب کاربری{% endblock %} 5 | 6 | {% block meta_description %}ورود یا ایجاد حساب کاربری جدید در کد هاب{% endblock %} 7 | 8 | {% block meta_og_description %}ورود یا ایجاد حساب کاربری جدید در کد هاب{% endblock %} 9 | 10 | {% block meta_og_url %}{% url 'login' %}{% endblock %} 11 | 12 | {% block meta_tw_title %}ورود به حساب کاربری{% endblock %} 13 | 14 | {% block meta_tw_description %}ورود یا ایجاد حساب کاربری جدید در کد هاب{% endblock %} 15 | 16 | {% block meta_tw_url %}{% url 'login' %}{% endblock %} 17 | 18 | {% block title %}ورود به حساب کاربری{% endblock %} 19 | 20 | {% block carousel_image %}{% static 'img/login-banner.png' %}{% endblock %} 21 | 22 | {% block content %} 23 |
24 |

ورود به حساب کاربری


25 |
26 | {% csrf_token %} 27 | {{ form.as_p }} 28 |   29 | ساخت حساب کاربری جدید 30 |
31 |
32 | {% endblock %} -------------------------------------------------------------------------------- /accounts/templates/registration/signup.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load crispy_forms_tags %} 3 | {% load static %} 4 | 5 | {% block meta_og_title %}ساخت حساب کاربری جدید{% endblock %} 6 | 7 | {% block meta_description %}با ایجاد حساب کاربری، به راحتی به برچسب ها نظر بدین و برچسب های خودتون رو ایجاد کنید! 8 | {% endblock %} 9 | 10 | {% block meta_og_description %}با ایجاد حساب کاربری، به راحتی به برچسب ها نظر بدین و برچسب های خودتون رو ایجاد کنید! 11 | {% endblock %} 12 | 13 | {% block meta_og_url %}{% url 'signup' %}{% endblock %} 14 | 15 | {% block meta_tw_title %}ساخت حساب کاربری جدید{% endblock %} 16 | 17 | {% block meta_tw_description %}با ایجاد حساب کاربری، به راحتی به برچسب ها نظر بدین و برچسب های خودتون رو ایجاد کنید! 18 | {% endblock %} 19 | 20 | {% block meta_tw_url %}{% url 'signup' %}{% endblock %} 21 | 22 | {% block title %}ساخت حساب کاربری جدید{% endblock %} 23 | 24 | {% block carousel_image %}{% static 'img/signup-banner.png' %}{% endblock %} 25 | 26 | {% block content %} 27 |

ساخت حساب کاربری


28 | {% crispy form %} 29 | {% endblock %} -------------------------------------------------------------------------------- /accounts/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/accounts/tests/__init__.py -------------------------------------------------------------------------------- /accounts/tests/test_forms.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/accounts/tests/test_forms.py -------------------------------------------------------------------------------- /accounts/tests/test_models.py: -------------------------------------------------------------------------------- 1 | from accounts.models import User # Codehub custom user model 2 | from django.contrib.auth import login 3 | from django.test import TestCase 4 | 5 | 6 | class UserTestCase(TestCase): 7 | 8 | @classmethod 9 | def setUpTestData(cls) -> None: 10 | cls.sample = { 11 | 'username': 'JohnDoe', 12 | 'email': 'john@doe.com', 13 | 'display_name': 'John', 14 | 'password': 'test123!', 15 | } 16 | 17 | def setUp(self) -> None: 18 | self.user = User.objects.create(**self.sample) 19 | 20 | def test_if_obj_returns_same_fields(self): 21 | fields = { 22 | 'username': self.user.username, 23 | 'email': self.user.email, 24 | 'display_name': self.user.display_name, 25 | 'password': self.user.password, 26 | } 27 | print(self.user.password) 28 | #self.assertEqual(fields, self.sample) 29 | 30 | # TODO: testing user authentication 31 | #def test_if_user_can_login(self): 32 | # pass 33 | -------------------------------------------------------------------------------- /accounts/tests/test_views.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/accounts/tests/test_views.py -------------------------------------------------------------------------------- /accounts/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from .views import (CustomLoginView, CustomPasswordChangeView, ProfileView, 4 | SignUpView) 5 | 6 | urlpatterns = [ 7 | path('signup/', SignUpView.as_view(), name='signup'), 8 | path('profile/', ProfileView.as_view(), name='profile'), 9 | path('password/', CustomPasswordChangeView.as_view(), name='change_password'), 10 | ] 11 | -------------------------------------------------------------------------------- /accounts/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.mixins import LoginRequiredMixin 2 | from django.contrib.auth.views import LoginView, PasswordChangeView 3 | from django.core.exceptions import PermissionDenied 4 | from django.http import HttpRequest, HttpResponse, request 5 | from django.shortcuts import render 6 | from django.urls import reverse_lazy 7 | from django.views.generic import CreateView, UpdateView 8 | from snippets.models import Snippet 9 | from tickets.models import Comment, Ticket 10 | 11 | from .forms import (CustomPasswordChangeForm, CustomUserCreationForm, 12 | CustomUserUpdateForm) 13 | from .models import User 14 | 15 | 16 | class SignUpView(CreateView): 17 | form_class = CustomUserCreationForm 18 | success_url = reverse_lazy('login') 19 | template_name = 'registration/signup.html' 20 | 21 | def get(self, request: HttpRequest, *args: str, **kwargs) -> HttpResponse: 22 | if request.user.is_authenticated: 23 | raise PermissionDenied() 24 | else: 25 | return super().get(request, *args, **kwargs) 26 | 27 | 28 | class ProfileView(LoginRequiredMixin, UpdateView): 29 | model = User 30 | form_class = CustomUserUpdateForm 31 | template_name = "profile.html" 32 | success_url = reverse_lazy('profile') 33 | 34 | def get_object(self, *args, **kwargs): 35 | user = User.objects.get(username=self.request.user) 36 | return user 37 | 38 | def get_context_data(self, *args, **kwargs): 39 | context = super(ProfileView, self).get_context_data(*args, **kwargs) 40 | 41 | context['snippets'] = Snippet.objects.filter( 42 | created_by=self.request.user.id 43 | ).order_by('-created_on') 44 | 45 | context['tickets'] = Ticket.objects.filter( 46 | created_by=self.request.user.id 47 | ).order_by('-created_on') 48 | 49 | context['comments'] = Comment.objects.filter( 50 | created_by=self.request.user.id 51 | ).order_by('-created_on') 52 | 53 | return context 54 | 55 | 56 | class CustomPasswordChangeView(LoginRequiredMixin, PasswordChangeView): 57 | template_name = 'registration/change_password.html' 58 | success_url = reverse_lazy('home') 59 | form_class = CustomPasswordChangeForm 60 | 61 | 62 | class CustomLoginView(LoginView): 63 | def get(self, request: HttpRequest, *args: str, **kwargs): 64 | if request.user.is_authenticated: 65 | raise PermissionDenied() 66 | else: 67 | return super().get(request, *args, **kwargs) 68 | -------------------------------------------------------------------------------- /codehub/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/codehub/__init__.py -------------------------------------------------------------------------------- /codehub/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for codehub project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'codehub.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /codehub/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for codehub project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.9. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | from decouple import config 16 | from django.urls import reverse_lazy 17 | 18 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 19 | BASE_DIR = Path(__file__).resolve().parent.parent 20 | 21 | 22 | # Quick-start development settings - unsuitable for production 23 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 24 | 25 | # SECURITY WARNING: keep the secret key used in production secret! 26 | SECRET_KEY = config('SECRET_KEY') 27 | 28 | # SECURITY WARNING: don't run with debug turned on in production! 29 | DEBUG = False 30 | 31 | ALLOWED_HOSTS = ['codehub.pythonanywhere.com'] 32 | 33 | 34 | # Application definition 35 | 36 | INSTALLED_APPS = [ 37 | 'django.contrib.admin', 38 | 'django.contrib.auth', 39 | 'django.contrib.contenttypes', 40 | 'django.contrib.sessions', 41 | 'django.contrib.messages', 42 | 'django.contrib.staticfiles', 43 | 44 | # apps 45 | 'main', 46 | 'accounts', 47 | 'snippets', 48 | 'tickets', 49 | 50 | # 3rd party pkgs 51 | 'django_jalali', 52 | 'crispy_forms', 53 | 'pygmentify', 54 | 55 | # drf 56 | 'rest_framework', 57 | 'drf_yasg', # docs 58 | ] 59 | 60 | MIDDLEWARE = [ 61 | 'django.middleware.security.SecurityMiddleware', 62 | 'django.contrib.sessions.middleware.SessionMiddleware', 63 | 'django.middleware.common.CommonMiddleware', 64 | 'django.middleware.csrf.CsrfViewMiddleware', 65 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 66 | 'django.contrib.messages.middleware.MessageMiddleware', 67 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 68 | ] 69 | 70 | ROOT_URLCONF = 'codehub.urls' 71 | 72 | TEMPLATES = [ 73 | { 74 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 75 | 'DIRS': [str(BASE_DIR.joinpath('templates'))], 76 | 'APP_DIRS': True, 77 | 'OPTIONS': { 78 | 'context_processors': [ 79 | 'django.template.context_processors.debug', 80 | 'django.template.context_processors.request', 81 | 'django.contrib.auth.context_processors.auth', 82 | 'django.contrib.messages.context_processors.messages', 83 | 84 | # custom event context 85 | 'main.context_processor.allEvents', 86 | ], 87 | }, 88 | }, 89 | ] 90 | 91 | WSGI_APPLICATION = 'codehub.wsgi.application' 92 | 93 | 94 | # Database 95 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 96 | 97 | DATABASES = { 98 | 'default': { 99 | 'ENGINE': 'django.db.backends.sqlite3', 100 | 'NAME': BASE_DIR / 'db.sqlite3', 101 | } 102 | } 103 | 104 | 105 | # Password validation 106 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 107 | 108 | AUTH_PASSWORD_VALIDATORS = [ 109 | { 110 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 111 | }, 112 | { 113 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 114 | }, 115 | { 116 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 117 | }, 118 | { 119 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 120 | }, 121 | ] 122 | 123 | 124 | # Internationalization 125 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 126 | 127 | LANGUAGE_CODE = 'fa-ir' 128 | 129 | TIME_ZONE = 'Asia/Tehran' 130 | 131 | USE_I18N = True 132 | 133 | USE_L10N = True 134 | 135 | # Static files (CSS, JavaScript, Images) 136 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 137 | 138 | STATIC_URL = '/static/' 139 | STATIC_ROOT = 'static/' 140 | 141 | MEDIA_URL = '/media/' 142 | MEDIA_ROOT = 'media/' 143 | 144 | # Default primary key field type 145 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 146 | 147 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 148 | 149 | # Crispy forms 150 | 151 | CRISPY_TEMPLATE_PACK = 'bootstrap4' 152 | 153 | # Localization + Time 154 | 155 | LOCALE_PATHS = [ 156 | BASE_DIR / 'locale', 157 | ] 158 | TIME_ZONE = 'Asia/Tehran' 159 | 160 | # User Authentication Setup 161 | 162 | AUTH_USER_MODEL = 'accounts.User' 163 | LOGIN_REDIRECT_URL = 'home' 164 | LOGOUT_REDIRECT_URL = 'home' 165 | LOGIN_URL = reverse_lazy('login') 166 | 167 | # Pygments configs 168 | PYGMENTIFY = { 169 | 'style': 'monokai', 170 | 'cssclass': 'highlight' 171 | } 172 | 173 | PYGMENTIFY_MINIFY = True 174 | 175 | # Overwriting the local configs 176 | 177 | try: 178 | from .local_settings import * 179 | except: 180 | pass 181 | -------------------------------------------------------------------------------- /codehub/urls.py: -------------------------------------------------------------------------------- 1 | """codehub URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from decouple import config 17 | from django.contrib import admin 18 | from django.urls import include, path 19 | 20 | urlpatterns = [ 21 | path(config('ADMIN_PATH'), admin.site.urls), 22 | 23 | # Accounting URLs 24 | path('', include('accounts.urls')), 25 | path('', include('django.contrib.auth.urls')), 26 | 27 | # URLs 28 | path('', include('main.urls')), 29 | path('', include('snippets.urls')), 30 | path('', include('tickets.urls')), 31 | ] 32 | -------------------------------------------------------------------------------- /codehub/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for codehub project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'codehub.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /locale/fa/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/locale/fa/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /locale/fa/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2022-03-23 20:14+0430\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 20 | 21 | #: accounts/forms.py:25 22 | msgid "Signup" 23 | msgstr "ثبت‌نام" 24 | 25 | #: accounts/forms.py:48 accounts/forms.py:65 26 | msgid "Update" 27 | msgstr "بروزرسانی" 28 | 29 | #: accounts/models.py:9 30 | msgid "Display Name" 31 | msgstr "نام قابل نمایش" 32 | 33 | #: accounts/models.py:13 34 | msgid "Email Address" 35 | msgstr "آدرس ایمیل" 36 | 37 | #: accounts/models.py:16 38 | msgid "Twitter Address" 39 | msgstr "آدرس توئیتر" 40 | 41 | #: accounts/models.py:20 42 | msgid "Github Address" 43 | msgstr "آدرس گیت هاب" 44 | 45 | #: main/forms.py:25 main/forms.py:49 snippets/forms.py:25 tickets/forms.py:29 46 | msgid "Create" 47 | msgstr "بساز" 48 | 49 | #: main/forms.py:42 tickets/forms.py:22 50 | msgid "How can I convert `count` variable in.." 51 | msgstr "" 52 | "**چجوری** متغیر `count` در کدم رو تغییر بدم..\n" 53 | "\r```\n" 54 | "int count = 5;\n" 55 | "System.Console.WriteLine(count); // should be String\n" 56 | "```" 57 | 58 | #: main/forms.py:66 tickets/forms.py:46 59 | msgid "Comment" 60 | msgstr "ثبت نظر" 61 | 62 | #: snippets/models.py:20 tickets/models.py:35 63 | msgid "Title" 64 | msgstr "عنوان" 65 | 66 | #: snippets/models.py:25 tickets/models.py:39 67 | msgid "Description" 68 | msgstr "توضیحات" 69 | 70 | #: snippets/models.py:29 71 | msgid "Script" 72 | msgstr "کد" 73 | 74 | #: snippets/models.py:32 75 | msgid "Programming Language" 76 | msgstr "زبان برنامه‌نویسی" 77 | 78 | #: tickets/constants.py:11 79 | msgid "Reject" 80 | msgstr "رد" 81 | 82 | #: tickets/constants.py:12 83 | msgid "Approve" 84 | msgstr "تائید" 85 | 86 | #: tickets/constants.py:13 87 | msgid "Pending" 88 | msgstr "در انتظار" 89 | 90 | #: tickets/models.py:43 91 | msgid "Tags" 92 | msgstr "تگ ها" 93 | 94 | #: tickets/models.py:49 95 | msgid "Validation" 96 | msgstr "اعتبارسنجی" 97 | 98 | #: tickets/models.py:83 99 | msgid "Comment Body" 100 | msgstr "نظر" 101 | 102 | #~ msgid "Body" 103 | #~ msgstr "بدنه" 104 | 105 | #~ msgid "Ticket will be \"Pending\" if you keep this field empty." 106 | #~ msgstr "در صورتی که این فیلد خالی بماند، برچسب در حالت انتظار قرار می گیرد." 107 | -------------------------------------------------------------------------------- /main/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/__init__.py -------------------------------------------------------------------------------- /main/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from snippets.models import Snippet 3 | from tickets.models import Comment, Tag, Ticket 4 | 5 | from .models import Event 6 | 7 | admin.site.register(Snippet) 8 | admin.site.register(Ticket) 9 | admin.site.register(Comment) 10 | admin.site.register(Tag) 11 | admin.site.register(Event) 12 | -------------------------------------------------------------------------------- /main/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MainConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'main' 7 | -------------------------------------------------------------------------------- /main/constants.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This file contains all sort of data/configs 3 | that are beings used in the main application 4 | ''' 5 | 6 | # ReDoc description 7 | REDOC_DESCRIPTION = ''' 8 | Codehub provides users/devs multiple RESTful API services. This RESTful service 9 | allows developers to work with CodeHub Web Services on any platform and machine. 10 | Make sure you follow the documentation and observe all conditions and requirements. 11 | ''' -------------------------------------------------------------------------------- /main/context_processor.py: -------------------------------------------------------------------------------- 1 | from .models import Event 2 | 3 | 4 | def allEvents(request): 5 | return { 6 | 'events': Event.objects.all().order_by('-created_on')[:5], 7 | } 8 | -------------------------------------------------------------------------------- /main/forms.py: -------------------------------------------------------------------------------- 1 | from crispy_forms.helper import FormHelper 2 | from crispy_forms.layout import Submit 3 | from django.forms.models import ModelForm 4 | from django.utils.translation import gettext as _ 5 | 6 | from .models import Comment, Snippet, Ticket 7 | 8 | 9 | class SnippetCreateForm(ModelForm): 10 | 11 | def __init__(self, *args, **kwargs): 12 | super().__init__(*args, **kwargs) 13 | 14 | for visible in self.visible_fields(): 15 | visible.field.widget.attrs['class'] = 'crispy-form-item' 16 | visible.field.widget.attrs['spellcheck'] = 'false' 17 | 18 | self.fields['body'].widget.attrs['class'] += ' code-snippet' 19 | 20 | class Meta: 21 | model = Snippet 22 | fields = ('title', 'description', 'body', 'lang') 23 | 24 | helper = FormHelper() 25 | helper.add_input(Submit('submit', _('Create'), 26 | css_class='btn-primary crispy-form-item')) 27 | 28 | 29 | class TicketCreateForm(ModelForm): 30 | 31 | def __init__(self, *args, **kwargs): 32 | super().__init__(*args, **kwargs) 33 | 34 | # 35 | #
36 | 37 | for visible in self.visible_fields(): 38 | visible.field.widget.attrs['class'] = 'crispy-form-item' 39 | visible.field.widget.attrs['spellcheck'] = 'false' 40 | 41 | self.fields['description'].widget.attrs['placeholder'] = _( 42 | 'How can I convert `count` variable in..') 43 | 44 | class Meta: 45 | model = Ticket 46 | fields = ('title', 'description', 'tags') 47 | 48 | helper = FormHelper() 49 | helper.add_input(Submit('submit', _('Create'), 50 | css_class='btn-primary crispy-form-item')) 51 | 52 | 53 | class CommentCreateForm(ModelForm): 54 | def __init__(self, *args, **kwargs): 55 | super().__init__(*args, **kwargs) 56 | 57 | for visible in self.visible_fields(): 58 | visible.field.widget.attrs['class'] = 'crispy-form-item' 59 | visible.field.widget.attrs['spellcheck'] = 'false' 60 | 61 | class Meta: 62 | model = Comment 63 | fields = ('body',) 64 | 65 | helper = FormHelper() 66 | helper.add_input(Submit('submit', _('Comment'), 67 | css_class='btn-primary crispy-form-item')) 68 | -------------------------------------------------------------------------------- /main/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.3 on 2022-03-25 01:30 2 | 3 | from django.db import migrations, models 4 | import django_jalali.db.models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Event', 17 | fields=[ 18 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('title', models.CharField(max_length=150)), 20 | ('body', models.TextField(blank=True)), 21 | ('created_on', django_jalali.db.models.jDateTimeField(auto_now=True)), 22 | ], 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /main/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/migrations/__init__.py -------------------------------------------------------------------------------- /main/models.py: -------------------------------------------------------------------------------- 1 | from accounts.models import User 2 | from django.db import models 3 | from django.urls import reverse 4 | from django.utils.text import slugify 5 | from django.utils.translation import gettext as _ 6 | from django_jalali.db import models as jmodels 7 | 8 | #from .constants import VERIFICATIONS 9 | 10 | 11 | 12 | class Event(models.Model): 13 | title = models.CharField( 14 | max_length=150, 15 | ) 16 | body = models.TextField(blank=True) 17 | created_on = jmodels.jDateTimeField( 18 | auto_now=True, 19 | editable=False, 20 | ) 21 | 22 | def __str__(self): 23 | return self.title 24 | -------------------------------------------------------------------------------- /main/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from .models import Event 4 | 5 | 6 | class EventSerializer(serializers.ModelSerializer): 7 | 8 | class Meta: 9 | model = Event 10 | fields = ('title', 'body', 'created_on') 11 | -------------------------------------------------------------------------------- /main/static/css/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: vazir; 3 | src: url('../fonts/vazir/Farsi-Digits/Vazir-Medium-FD.eot'); 4 | src: url('../fonts/vazir/Farsi-Digits/Vazir-Medium-FD.eot?#iefix') format('embedded-opentype'), 5 | url('../fonts/vazir/Farsi-Digits/Vazir-Medium-FD.woff2') format('woff2'), 6 | url('../fonts/vazir/Farsi-Digits/Vazir-Medium-FD.woff') format('woff'), 7 | url('../fonts/vazir/Farsi-Digits/Vazir-Medium-FD.ttf') format('truetype'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | @font-face { 13 | font-family: ubuntumono; 14 | src: url('../fonts/ubuntu/UbuntuMono-Regular.ttf') format('truetype'); 15 | } 16 | 17 | body { 18 | font-family: vazir; 19 | } 20 | 21 | .crispy-form-item { 22 | margin: 10px; 23 | } 24 | 25 | .new-feature-badge { 26 | background: rgb(255,0,204); 27 | background: linear-gradient(204deg, rgba(255,0,204,1) 0%, rgba(0,16,255,1) 100%); 28 | } 29 | 30 | .code-snippet { 31 | text-align: left; 32 | font-family: ubuntumono; 33 | text-decoration: none; 34 | direction: ltr; 35 | } 36 | 37 | .pre-snippet { 38 | font-size: 17px; 39 | background-color: #292d3e; 40 | padding: 17px; 41 | border-radius: 5px; 42 | white-space: pre-wrap; 43 | word-wrap: break-word; 44 | text-align: justify; 45 | unicode-bidi: embed; 46 | } 47 | 48 | .code-snippet-lang { 49 | position: absolute; 50 | right: auto; 51 | top: auto; 52 | width: 35px; 53 | padding: 4px; 54 | background-color: #fff; 55 | border-radius: 20px; 56 | margin: 8px; 57 | transition: opacity .3s; 58 | } 59 | 60 | .code-snippet-lang:hover { 61 | opacity: 0.1; 62 | } 63 | 64 | .offcanvas-codehub { 65 | background-image: url("../img/offcanvas.png"); 66 | background-size: 80px; 67 | background-repeat: no-repeat; 68 | background-position: center; 69 | } 70 | 71 | .code-snippet span.linenos { 72 | color: #969699; 73 | margin-right: 20px; 74 | user-select: none; 75 | } 76 | 77 | .ticket-description blockquote{ 78 | border-right: solid thin; 79 | color:#afafaf; 80 | font-style: italic; 81 | padding: 10px 15px 1px 0px; 82 | } 83 | 84 | .ticket-description p code{ 85 | font-family: ubuntumono; 86 | background-color: #292d3e; 87 | padding: 3px; 88 | color: #fff; 89 | border-radius: 5px; 90 | } 91 | 92 | .ticket-description img{ 93 | width: 100%; 94 | } 95 | 96 | .ticket-description pre { 97 | font-size: 17px; 98 | color: #292d3e; 99 | background-color: #f8f9fa; 100 | padding: 17px; 101 | border-radius: 5px; 102 | white-space: pre-wrap; 103 | word-wrap: break-word; 104 | text-align: justify; 105 | } 106 | -------------------------------------------------------------------------------- /main/static/fonts/ubuntu/UbuntuMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/ubuntu/UbuntuMono-Bold.ttf -------------------------------------------------------------------------------- /main/static/fonts/ubuntu/UbuntuMono-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/ubuntu/UbuntuMono-BoldItalic.ttf -------------------------------------------------------------------------------- /main/static/fonts/ubuntu/UbuntuMono-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/ubuntu/UbuntuMono-Italic.ttf -------------------------------------------------------------------------------- /main/static/fonts/ubuntu/UbuntuMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/ubuntu/UbuntuMono-Regular.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Black-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Black-FD.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Black-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Black-FD.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Black-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Black-FD.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Black-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Black-FD.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Bold-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Bold-FD.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Bold-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Bold-FD.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Bold-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Bold-FD.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Bold-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Bold-FD.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Light-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Light-FD.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Light-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Light-FD.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Light-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Light-FD.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Light-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Light-FD.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Medium-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Medium-FD.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Medium-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Medium-FD.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Medium-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Medium-FD.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Medium-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Medium-FD.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Regular-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Regular-FD.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Regular-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Regular-FD.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Regular-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Regular-FD.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Regular-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Regular-FD.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Thin-FD.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Thin-FD.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Thin-FD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Thin-FD.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Thin-FD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Thin-FD.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Farsi-Digits/Vazir-Thin-FD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Farsi-Digits/Vazir-Thin-FD.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Black.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Black.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Black.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Black.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Black.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Bold.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Bold.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Bold.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Bold.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Light.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Light.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Light.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Light.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Medium.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Medium.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Medium.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Medium.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Regular.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Regular.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Regular.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Regular.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Thin.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Thin.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Thin.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Thin.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Thin.woff2 -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Variable.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Variable.eot -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Variable.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Variable.ttf -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Variable.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Variable.woff -------------------------------------------------------------------------------- /main/static/fonts/vazir/Vazir-Variable.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/fonts/vazir/Vazir-Variable.woff2 -------------------------------------------------------------------------------- /main/static/img/development-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/img/development-banner.png -------------------------------------------------------------------------------- /main/static/img/download-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/img/download-banner.png -------------------------------------------------------------------------------- /main/static/img/fav.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/img/fav.ico -------------------------------------------------------------------------------- /main/static/img/figure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/img/figure.png -------------------------------------------------------------------------------- /main/static/img/home-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/img/home-banner.png -------------------------------------------------------------------------------- /main/static/img/icons/material/arduino.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/bash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/c.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/cpp.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/csharp.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/css.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/dart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/docker.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/go.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/html.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/java.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/js.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/json.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/lua.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/md.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/mysql.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/php.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/python.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/icons/material/rb.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/img/logo.png -------------------------------------------------------------------------------- /main/static/img/offcanvas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/img/offcanvas.png -------------------------------------------------------------------------------- /main/static/img/solid-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/main/static/img/solid-banner.png -------------------------------------------------------------------------------- /main/templates/403.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | 3 | {% block title %}دسترسی محدود{% endblock %} 4 | 5 | {% block content %} 6 |

دسترسی شما محدود است!

7 |

متاسفانه در حال حاضر، دسترسی شما به این صفحه مقدور نمی باشد.

8 | {% endblock %} -------------------------------------------------------------------------------- /main/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | 3 | {% block title %}صفحه مورد نظر یافت نشد{% endblock %} 4 | 5 | {% block content %} 6 |

صفحه مورد نظر یافت نشد!

7 | {% endblock %} -------------------------------------------------------------------------------- /main/templates/development.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load static %} 3 | 4 | {% block meta_og_title %}خدمات کد هاب برای توسعه دهندگان{% endblock %} 5 | 6 | {% block meta_description %}ما علاوه بر تمام خدماتی که در اختیار شما قرار دادیم، یک سرویس Rest API هم برای توسعه دهندگان 7 | و برنامه نویس ها ایجاد 8 | کردیم تا به راحتی از خدمات کد هاب، روی هر پلتفورمی استفاده کنند و اپلیکیشن های خودشون رو توسعه بدن.{% endblock %} 9 | 10 | {% block meta_og_description %}ما علاوه بر تمام خدماتی که در اختیار شما قرار دادیم، یک سرویس Rest API هم برای توسعه 11 | دهندگان و برنامه نویس ها ایجاد 12 | کردیم تا به راحتی از خدمات کد هاب، روی هر پلتفورمی استفاده کنند و اپلیکیشن های خودشون رو توسعه بدن.{% endblock %} 13 | 14 | {% block meta_og_url %}{% url 'development' %}{% endblock %} 15 | 16 | {% block meta_tw_title %}خدمات کد هاب برای توسعه دهندگان{% endblock %} 17 | 18 | {% block meta_tw_description %}ما علاوه بر تمام خدماتی که در اختیار شما قرار دادیم، یک سرویس Rest API هم برای توسعه 19 | دهندگان و برنامه نویس ها ایجاد 20 | کردیم تا به راحتی از خدمات کد هاب، روی هر پلتفورمی استفاده کنند و اپلیکیشن های خودشون رو توسعه بدن.{% endblock %} 21 | 22 | {% block meta_tw_url %}{% url 'development' %}{% endblock %} 23 | 24 | {% block development_active %}active{% endblock %} 25 | {% block title %}کد هاب برای توسعه دهندگان{% endblock %} 26 | 27 | {% block carousel_image %}{% static 'img/development-banner.png' %}{% endblock %} 28 | 29 | {% block content %} 30 |

کد هاب برای توسعه دهندگان

31 |

32 | ما علاوه بر تمام خدماتی که در اختیار شما قرار دادیم، یک سرویس Rest API هم برای توسعه دهندگان و برنامه نویس ها ایجاد 33 | کردیم تا به راحتی از خدمات کد هاب، روی هر پلتفورمی استفاده کنند و اپلیکیشن های خودشون رو توسعه بدن. از لیست زیر، 34 | داکیومنت مورد نظر خودتون رو بررسی کنید. 35 |


36 | 37 | 58 | {% endblock %} -------------------------------------------------------------------------------- /main/templates/download.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load static %} 3 | 4 | {% block meta_og_title %}کد هاب در دیگر پلتفورم ها{% endblock %} 5 | 6 | {% block meta_description %}به راحتی با استفاده از دیگر محصولات تیم کد هاب، به تمامی سرویس ها و قابلیت ها، در دیگر 7 | پلتفورم ها دسترسی داشته 8 | باشید.{% endblock %} 9 | 10 | {% block meta_og_description %}به راحتی با استفاده از دیگر محصولات تیم کد هاب، به تمامی سرویس ها و قابلیت ها، در دیگر 11 | پلتفورم ها دسترسی داشته 12 | باشید.{% endblock %} 13 | 14 | {% block meta_og_url %}{% url 'download' %}{% endblock %} 15 | 16 | {% block meta_tw_title %}کد هاب در دیگر پلتفورم ها{% endblock %} 17 | 18 | {% block meta_tw_description %}به راحتی با استفاده از دیگر محصولات تیم کد هاب، به تمامی سرویس ها و قابلیت ها، در دیگر 19 | پلتفورم ها دسترسی داشته 20 | باشید.{% endblock %} 21 | 22 | {% block meta_tw_url %}{% url 'download' %}{% endblock %} 23 | 24 | {% block download_active %}active{% endblock %} 25 | {% block title %}کد هاب در دیگر پلتفورم ها{% endblock %} 26 | 27 | {% block carousel_image %}{% static 'img/download-banner.png' %}{% endblock %} 28 | 29 | {% block content %} 30 |

کد هاب در دیگر پلتفورم ها

31 |

32 | به راحتی با استفاده از دیگر محصولات تیم کد هاب، به تمامی سرویس ها و قابلیت ها، در دیگر پلتفورم ها دسترسی داشته 33 | باشید. 34 |

35 | 36 |
37 |
38 |
39 |
40 | 42 |
43 |
44 | 46 |
47 |
48 | 50 |
51 |
52 |
53 |
54 | {% endblock %} -------------------------------------------------------------------------------- /main/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load static %} 3 | 4 | {% block carousel_image %}{% static 'img/home-banner.png' %}{% endblock %} 5 | 6 | {% block content %} 7 |

سرویس متن‌باز و رایگان کد هاب!

8 |

9 | کُد هاب (به انگلیسی CodeHub) اولین سرویس متن نشان پارسیه که بصورت متن باز در پلتفورم گیت هاب در حال توسعه و بزرگ 10 | شدنه. در توسعه این سرویس، از چارچوب Django بعنوان بک‌اند و Bootstrap بعنوان فرانت‌اند استفاده شده. 11 |


12 | 13 |
14 |
15 |
16 |
17 | .. 19 |
کد هاب، ساخته شده با استفاده از جنگو و بوت استرپ
20 |
21 |
22 |
23 |

24 | 25 |

مشارکت در پروژه

26 |

27 | کد هاب بصورت متن باز در حال توسعه هست. این به این معنیه که هر توسعه دهنده ای میتونه در گسترش این پروژه سهیم باشه. 28 | برای وارد شدن به گیت هاب این پروژه، کافیه تنها روی عکس زیر کلیک کنید. در ضمن، تمامی پول رکوئست های ساخته شده توسط 29 | شما، قابل ستایش و قدردانیه. با ستاره دادن و حمایت از پروژه، به بزرگتر شدن کامیونیتی متن باز ایران کمک کنید. 30 |


31 | 32 |
33 |
34 |
35 |
36 | 37 | Repository 39 | 40 |
مخزن اصلی کد هاب در گیت هاب
41 |
42 |
43 |
44 |

45 | 46 |

کمی درباره من..!

47 |

48 | نسخه قبلی کد هاب، اولین پروژه جنگویی من بعد از ۲ ماه شروع سفر من در جنگو بود. پر از باگ های امنیتی، مشکلات دیزاین 49 | و.. اما بعد از مدتی حسابی کار کردن روی نسخه جدید، با فیچر های نو و قابلیت های خفن، دوباره کد هاب رو سر پا کردم تا 50 | خدمتی (هرچند کوچک) به جامعه متن باز ایران کرده باشم. بزرگترین هدف من بعنوان توسعه دهنده اصلی کد هاب، ترویج فرهنگ 51 | نشر 52 | دانشه. 53 |

54 |

من صدرا یحیی پور هستم. برنامه نویس پایتون و توسعه دهنده نرم افزار متن باز، علاقهمند به یادگیری ماشین و 55 | خالق کد هاب. برای‌ آشنایی بیشتر با من، گیت هاب من رو از لینک زیر دنبال کنید. :)


56 | 57 |
58 |
59 |
60 |
61 | 62 | Profile 64 | 65 |
پروفایل من در گیت هاب
66 |
67 |
68 |
69 |

70 | 71 |

در نهایت، بسیار سپاسگزارم از تمام کسانی که طی این سال ها حامی و مشوق من بودن و همیشه بدون هیچ چشم داشتی، در کنارم 72 | حضور داشتن. ممنونم از همه دوستانی که کد هاب رو حمایت کردن و در توسعه این پروژه، نقش داشتند.


73 | 74 |
75 |
76 |
77 |
78 | 79 | Contributors 81 | 82 |
توسعه دهندگان
83 |
84 |
85 |
86 |

87 | 88 | {% if tickets %} 89 |

جدید ترین برچسب ها

90 | {% for ticket in tickets %} 91 |
92 |
93 | 94 |
{{ticket.title}}
95 |

ثبت شده در {{ticket.created_on |date:'d-m-Y'}} توسط 96 | {{ticket.created_by}} 97 |

98 |
99 |
100 | {% endfor %} 101 | {% endif %} 102 | {% endblock %} -------------------------------------------------------------------------------- /main/templatetags/markdown_extras.py: -------------------------------------------------------------------------------- 1 | import markdown as md 2 | from django import template 3 | from django.template.defaultfilters import stringfilter 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.filter() 9 | @stringfilter 10 | def markdown(value): 11 | return md.markdown(value, extensions=['markdown.extensions.fenced_code']) 12 | 13 | -------------------------------------------------------------------------------- /main/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from django.views.generic import TemplateView 3 | from drf_yasg import openapi 4 | from drf_yasg.views import get_schema_view 5 | from rest_framework import permissions 6 | 7 | from .constants import REDOC_DESCRIPTION 8 | from .views import HomeView, ListEventsAPIView 9 | 10 | schema_view = get_schema_view( 11 | openapi.Info( 12 | title='CodeHub RESTful API Service Documentation', 13 | default_version='v1', 14 | description=REDOC_DESCRIPTION, 15 | contact=openapi.Contact(email='lnxpylnxpy@gmail.com'), 16 | license=openapi.License(name='MIT License'), 17 | ), 18 | public=True, 19 | permission_classes=[permissions.AllowAny], 20 | ) 21 | 22 | urlpatterns = [ 23 | 24 | # Template URLs 25 | path('', HomeView.as_view(), name='home'), 26 | 27 | # API URLs 28 | # -- v1.0 -- 29 | path('api/v1/event', ListEventsAPIView.as_view()), 30 | path('api/v1/docs', schema_view.with_ui('redoc', 31 | cache_timeout=0), name='api_docs_v1'), 32 | 33 | # static pages 34 | path( 35 | 'download/', 36 | TemplateView.as_view(template_name='download.html'), 37 | name='download' 38 | ), 39 | path( 40 | 'development/', 41 | TemplateView.as_view(template_name='development.html'), 42 | name='development' 43 | ), 44 | ] 45 | -------------------------------------------------------------------------------- /main/utils.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This file contains methods that are being 3 | used as a data generator in the models, views, and etc. 4 | ''' 5 | 6 | 7 | def generateUID(model, nb=5) -> str: 8 | from secrets import token_hex 9 | 10 | uid = token_hex(nbytes=nb) 11 | while model.objects.filter(id=uid).exists(): 12 | uid = token_hex(nbytes=nb) 13 | 14 | return uid 15 | -------------------------------------------------------------------------------- /main/views.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import ListView 2 | from rest_framework.generics import ListAPIView 3 | from tickets.models import Ticket 4 | 5 | from .models import Event 6 | from .serializers import EventSerializer 7 | 8 | 9 | # Template Views 10 | class HomeView(ListView): 11 | template_name = 'home.html' 12 | model = Ticket 13 | queryset = Ticket.objects.filter( 14 | is_valid='approved').order_by('-created_on')[:5] 15 | context_object_name = 'tickets' 16 | 17 | 18 | # API Views 19 | class ListEventsAPIView(ListAPIView): 20 | serializer_class = EventSerializer 21 | queryset = Event.objects.all().order_by('-created_on')[:5] 22 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'codehub.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.4.1 2 | autopep8==1.6.0 3 | beautifulsoup4==4.10.0 4 | certifi==2021.10.8 5 | charset-normalizer==2.0.9 6 | coreapi==2.3.3 7 | coreschema==0.0.4 8 | Django==4.0.3 9 | django-crispy-forms==1.13.0 10 | django-hashids==0.5.0 11 | django-jalali==5.0.0 12 | django-markdownx==3.0.1 13 | django-pygmentify==0.3.7 14 | djangorestframework==3.12.4 15 | drf-yasg==1.20.0 16 | hashids==1.3.1 17 | idna==3.3 18 | importlib-metadata==4.8.2 19 | inflection==0.5.1 20 | isort==5.10.1 21 | itypes==1.2.0 22 | jdatetime==3.7.0 23 | Jinja2==3.0.3 24 | Markdown==3.3.6 25 | MarkupSafe==2.0.1 26 | packaging==21.3 27 | Pillow==9.0.1 28 | pycodestyle==2.8.0 29 | Pygments==2.10.0 30 | pyparsing==3.0.6 31 | python-decouple==3.5 32 | pytz==2021.3 33 | requests==2.26.0 34 | ruamel.yaml==0.17.17 35 | ruamel.yaml.clib==0.2.6 36 | soupsieve==2.3.1 37 | sqlparse==0.4.2 38 | toml==0.10.2 39 | uritemplate==4.1.1 40 | urllib3==1.26.7 41 | zipp==3.6.0 42 | -------------------------------------------------------------------------------- /snippets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/snippets/__init__.py -------------------------------------------------------------------------------- /snippets/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /snippets/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SnippetsConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'snippets' 7 | -------------------------------------------------------------------------------- /snippets/constants.py: -------------------------------------------------------------------------------- 1 | # lang choices for Snippet.lang 2 | LANGUAGES = ( 3 | ('arduino', 'Arduino'), # + icon 4 | ('bash', 'Bash'), # + icon 5 | ('c', 'C'), # + icon 6 | ('cpp', 'C++'), # + icon 7 | ('csharp', 'C#'), # + icon 8 | ('css', 'CSS'), # + icon 9 | ('dart', 'Dart'), # + icon 10 | ('docker', 'DockerFile'), # + icon 11 | ('docker-compose', 'DockerCompose'), # + icon (same as docker.svg) 12 | ('go', 'Go'), # + icon 13 | ('html', 'HTML'), # + icon 14 | ('java', 'Java'), # + icon 15 | ('js', 'JavaScript'), # + icon 16 | ('json', 'JSON'), # + icon 17 | ('lua', 'Lua'), # + icon 18 | ('md', 'markdown'), # + icon 19 | ('mysql', 'MySQL'), # + icon 20 | ('php', 'PHP'), # + icon 21 | ('python', 'Python'), # + icon 22 | ('rb', 'Ruby'), # + icon 23 | ) -------------------------------------------------------------------------------- /snippets/forms.py: -------------------------------------------------------------------------------- 1 | from crispy_forms.helper import FormHelper 2 | from crispy_forms.layout import Submit 3 | from django.forms.models import ModelForm 4 | from django.utils.translation import gettext as _ 5 | 6 | from .models import Snippet 7 | 8 | 9 | class SnippetCreateForm(ModelForm): 10 | 11 | def __init__(self, *args, **kwargs): 12 | super().__init__(*args, **kwargs) 13 | 14 | for visible in self.visible_fields(): 15 | visible.field.widget.attrs['class'] = 'crispy-form-item' 16 | visible.field.widget.attrs['spellcheck'] = 'false' 17 | 18 | self.fields['body'].widget.attrs['class'] += ' code-snippet' 19 | 20 | class Meta: 21 | model = Snippet 22 | fields = ('title', 'description', 'body', 'lang') 23 | 24 | helper = FormHelper() 25 | helper.add_input(Submit('submit', _('Create'), 26 | css_class='btn-primary crispy-form-item')) -------------------------------------------------------------------------------- /snippets/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.3 on 2022-03-25 01:30 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import django_jalali.db.models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Snippet', 20 | fields=[ 21 | ('id', models.CharField(editable=False, max_length=11, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('title', models.CharField(default='', max_length=50, verbose_name='عنوان')), 23 | ('description', models.TextField(blank=True, verbose_name='توضیحات')), 24 | ('body', models.TextField(verbose_name='کد')), 25 | ('lang', models.CharField(choices=[('arduino', 'Arduino'), ('bash', 'Bash'), ('c', 'C'), ('cpp', 'C++'), ('csharp', 'C#'), ('css', 'CSS'), ('dart', 'Dart'), ('docker', 'DockerFile'), ('docker-compose', 'DockerCompose'), ('go', 'Go'), ('html', 'HTML'), ('java', 'Java'), ('js', 'JavaScript'), ('json', 'JSON'), ('lua', 'Lua'), ('md', 'markdown'), ('mysql', 'MySQL'), ('php', 'PHP'), ('python', 'Python'), ('rb', 'Ruby')], max_length=250, verbose_name='زبان برنامه\u200cنویسی')), 26 | ('created_on', django_jalali.db.models.jDateTimeField(auto_now=True)), 27 | ('created_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 28 | ], 29 | ), 30 | migrations.AddIndex( 31 | model_name='snippet', 32 | index=models.Index(fields=['id'], name='snippets_sn_id_828944_idx'), 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /snippets/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/snippets/migrations/__init__.py -------------------------------------------------------------------------------- /snippets/models.py: -------------------------------------------------------------------------------- 1 | from accounts.models import User 2 | from django.db import models 3 | from django.urls import reverse 4 | from django.utils.translation import gettext as _ 5 | from django_jalali.db import models as jmodels 6 | from main.utils import generateUID 7 | 8 | from .constants import LANGUAGES 9 | 10 | 11 | class Snippet(models.Model): 12 | 13 | id = models.CharField( 14 | verbose_name='ID', 15 | max_length=11, 16 | primary_key=True, 17 | editable=False, 18 | ) 19 | title = models.CharField( 20 | verbose_name=_('Title'), 21 | max_length=50, 22 | default='', 23 | ) 24 | description = models.TextField( 25 | verbose_name=_('Description'), 26 | blank=True 27 | ) 28 | body = models.TextField( 29 | verbose_name=_('Script'), 30 | ) 31 | lang = models.CharField( 32 | verbose_name=_('Programming Language'), 33 | max_length=250, 34 | choices=LANGUAGES, 35 | ) 36 | created_on = jmodels.jDateTimeField( 37 | auto_now=True, 38 | editable=False, 39 | ) 40 | created_by = models.ForeignKey( 41 | User, 42 | on_delete=models.CASCADE, 43 | editable=False, 44 | null=True, 45 | ) 46 | 47 | # TODO: Adding a view counter using django-hitcount pkg 48 | 49 | class Meta: 50 | indexes = [models.Index(fields=['id'])] 51 | 52 | def __str__(self): return self.title 53 | 54 | def get_absolute_url(self): 55 | return reverse('snippet', args=[str(self.id)]) 56 | 57 | def save(self, *args, **kwargs): 58 | if not self.id: 59 | self.id = generateUID(Snippet) 60 | super().save(*args, **kwargs) 61 | 62 | -------------------------------------------------------------------------------- /snippets/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from .models import Snippet 4 | 5 | 6 | class SnippetSerializer(serializers.ModelSerializer): 7 | 8 | class Meta: 9 | model = Snippet 10 | fields = ('id', 'title', 'description', 'body', 11 | 'lang', 'created_by', 'created_on') -------------------------------------------------------------------------------- /snippets/templates/create_snippet.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load crispy_forms_tags %} 3 | {% load static %} 4 | 5 | {% block meta_og_title %}ایجاد تکه‌کد جدید{% endblock %} 6 | 7 | {% block meta_description %}با ساخت تکه‌کد در کد هاب و اشتراک گذاری خطا های خود، سریعتر مشکل رو برطرف 8 | کنید!{% endblock %} 9 | 10 | {% block meta_og_description %}با ساخت تکه‌کد در کد هاب و اشتراک گذاری خطا های خود، سریعتر مشکل رو برطرف 11 | کنید!{% endblock %} 12 | 13 | {% block meta_og_url %}{% url 'new_snippet' %}{% endblock %} 14 | 15 | {% block meta_tw_title %}ایجاد تکه‌کد جدید{% endblock %} 16 | 17 | {% block meta_tw_description %}با ساخت تکه‌کد در کد هاب و اشتراک گذاری خطا های خود، سریعتر مشکل رو برطرف 18 | کنید!{% endblock %} 19 | 20 | {% block meta_tw_url %}{% url 'new_snippet' %}{% endblock %} 21 | 22 | {% block snippet_active %}active{% endblock %} 23 | {% block title %}ایجاد تکه‌کد جدید{% endblock %} 24 | 25 | {% block carousel_image %}{% static 'img/snippet-banner.png' %}{% endblock %} 26 | 27 | {% block content %} 28 | {% crispy form %} 29 | {% endblock %} -------------------------------------------------------------------------------- /snippets/templates/snippet.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load static %} 3 | {% load pygmentify_tags %} 4 | 5 | {% block meta_og_title %}{{snippet.title}}{% endblock %} 6 | 7 | {% block meta_description %}{{snippet.description}}{% endblock %} 8 | 9 | {% block meta_og_description %}{{snippet.description}}{% endblock %} 10 | 11 | {% block meta_og_url %}{{snippet.get_absolute_url}}{% endblock %} 12 | 13 | {% block meta_tw_title %}{{snippet.title}}{% endblock %} 14 | 15 | {% block meta_tw_description %}{{snippet.description}}{% endblock %} 16 | 17 | {% block meta_tw_url %}{{snippet.get_absolute_url}}{% endblock %} 18 | 19 | {% block title %}{{snippet.title}}{% endblock %} 20 | 21 | {% block script %} 22 | 23 | {% endblock %} 24 | 25 | {% block style %} 26 | {% endblock %} 27 | 28 | {% block content %} 29 |

{{snippet.title}}


33 | 34 | {% if snippet.description %} 35 |

{{snippet.description}}

36 | {% endif %} 37 | 38 | {% pygmentify linenos='inline' %} 39 | - 40 |
{{snippet.body}}
41 | {% endpygmentify %} 42 | ثبت شده در تاریخ {{snippet.created_on | date:'d-m-Y'}} 43 | {% endblock %} -------------------------------------------------------------------------------- /snippets/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /snippets/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/snippets/tests/__init__.py -------------------------------------------------------------------------------- /snippets/tests/test_models.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from snippets.constants import LANGUAGES 3 | from snippets.models import Snippet 4 | 5 | 6 | class SnippetTestCase(TestCase): 7 | 8 | @classmethod 9 | def setUpTestData(cls) -> None: 10 | cls.sample = { 11 | 'title': 'simple title', 12 | 'description': 'some description', 13 | 'body': 'code snippet', 14 | 'lang': LANGUAGES[0][1], # Arduino 15 | } 16 | 17 | def setUp(self) -> None: 18 | self.snippet = Snippet.objects.create(**self.sample) 19 | 20 | def test_if_obj_returns_same_fields(self): 21 | fields = { 22 | 'title': self.snippet.title, 23 | 'description': self.snippet.description, 24 | 'body': self.snippet.body, 25 | 'lang': self.snippet.lang, 26 | } 27 | self.assertEqual(fields, self.sample) 28 | 29 | def test_sid_length(self): 30 | # nbyte is set to 5 by default 31 | # which provides an id with 10 chars 32 | self.assertEqual(len(self.snippet.id), 10) -------------------------------------------------------------------------------- /snippets/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from .views import (SnippetAPIView, SnippetCreateAPIView, SnippetCreateView, 4 | SnippetView) 5 | 6 | urlpatterns = [ 7 | # Template URLs 8 | path('new-snippet/', SnippetCreateView.as_view(), name='new_snippet'), 9 | path('snippet//', SnippetView.as_view(), name='snippet'), 10 | 11 | # API URLs 12 | # -- v1.0 -- 13 | path('api/v1/snippet', SnippetCreateAPIView.as_view()), 14 | path('api/v1/snippet/', SnippetAPIView.as_view()), 15 | ] 16 | -------------------------------------------------------------------------------- /snippets/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import get_object_or_404 2 | from django.views.generic import CreateView, DetailView 3 | from rest_framework.generics import CreateAPIView, ListAPIView, RetrieveAPIView 4 | 5 | from .forms import SnippetCreateForm 6 | from .models import Snippet 7 | from .serializers import SnippetSerializer 8 | 9 | # Template Views 10 | 11 | class SnippetCreateView(CreateView): 12 | model = Snippet 13 | template_name = 'create_snippet.html' 14 | form_class = SnippetCreateForm 15 | 16 | def form_valid(self, form): 17 | if self.request.user.is_authenticated: 18 | user = self.request.user 19 | form.instance.created_by = user 20 | return super().form_valid(form) 21 | 22 | 23 | class SnippetView(DetailView): 24 | model = Snippet 25 | template_name = 'snippet.html' 26 | 27 | 28 | # API Views 29 | 30 | class SnippetCreateAPIView(CreateAPIView): 31 | serializer_class = SnippetSerializer 32 | 33 | 34 | class SnippetAPIView(RetrieveAPIView): 35 | serializer_class = SnippetSerializer 36 | 37 | def get_object(self): 38 | requested_uid = self.kwargs['pk'] 39 | return get_object_or_404(Snippet, id=requested_uid) -------------------------------------------------------------------------------- /templates/_base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | {% block title %}کد هاب، اولین سرویس متن‌نشان پارسی{% endblock %} 13 | 14 | 16 | 17 | 18 | 19 | 20 | 23 | {% block script %}{% endblock %} 24 | 25 | {% block style %}{% endblock %} 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 103 | 104 | 114 | 115 |
116 |
117 |
118 | {% block content %}{% endblock %} 119 |
120 |
121 |
122 | 123 |
124 |
125 |
126 |

127 | کد 128 | هاب 129 | ساخته شده 130 | با 💙 131 |

132 |
133 |
134 |
135 | 136 |
138 |
139 |
جدیدترین رویداد ها
140 | 141 |
142 |
143 | {% if events.exists %} 144 | {% for event in events %} 145 |
146 |
{{event.title}} 147 | {{event.created_on | date:'Y-m-d'}}
148 |

{{event.body}}

149 |
150 | {% endfor %} 151 | {% else %} 152 |

رویدادی موجود نیست!

153 | {% endif %} 154 |
155 |
156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /tickets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/tickets/__init__.py -------------------------------------------------------------------------------- /tickets/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /tickets/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class TicketsConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'tickets' 7 | -------------------------------------------------------------------------------- /tickets/constants.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This file contains all sort of data/configs 3 | that are beings used in the main application 4 | ''' 5 | 6 | from django.utils.translation import gettext as _ 7 | 8 | # verification choices for Ticket 9 | # and Comment is_verified 10 | VERIFICATIONS = ( 11 | ('rejected', _('Reject')), 12 | ('approved', _('Approve')), 13 | ('pending', _('Pending')), 14 | ) -------------------------------------------------------------------------------- /tickets/forms.py: -------------------------------------------------------------------------------- 1 | from crispy_forms.helper import FormHelper 2 | from crispy_forms.layout import Submit 3 | from django.forms.models import ModelForm 4 | from django.utils.translation import gettext as _ 5 | 6 | from .models import Comment, Ticket 7 | 8 | 9 | class TicketCreateForm(ModelForm): 10 | 11 | def __init__(self, *args, **kwargs): 12 | super().__init__(*args, **kwargs) 13 | 14 | # 15 | #
16 | 17 | for visible in self.visible_fields(): 18 | visible.field.widget.attrs['class'] = 'crispy-form-item' 19 | visible.field.widget.attrs['spellcheck'] = 'false' 20 | 21 | self.fields['description'].widget.attrs['placeholder'] = _( 22 | 'How can I convert `count` variable in..') 23 | 24 | class Meta: 25 | model = Ticket 26 | fields = ('title', 'description', 'tags') 27 | 28 | helper = FormHelper() 29 | helper.add_input(Submit('submit', _('Create'), 30 | css_class='btn-primary crispy-form-item')) 31 | 32 | 33 | class CommentCreateForm(ModelForm): 34 | def __init__(self, *args, **kwargs): 35 | super().__init__(*args, **kwargs) 36 | 37 | for visible in self.visible_fields(): 38 | visible.field.widget.attrs['class'] = 'crispy-form-item' 39 | visible.field.widget.attrs['spellcheck'] = 'false' 40 | 41 | class Meta: 42 | model = Comment 43 | fields = ('body',) 44 | 45 | helper = FormHelper() 46 | helper.add_input(Submit('submit', _('Comment'), 47 | css_class='btn-primary crispy-form-item')) 48 | -------------------------------------------------------------------------------- /tickets/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.3 on 2022-03-25 01:30 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import django_jalali.db.models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Tag', 20 | fields=[ 21 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('name', models.CharField(max_length=50)), 23 | ('description', models.CharField(max_length=150)), 24 | ('created_on', django_jalali.db.models.jDateTimeField(auto_now=True)), 25 | ], 26 | ), 27 | migrations.CreateModel( 28 | name='Ticket', 29 | fields=[ 30 | ('id', models.CharField(editable=False, max_length=11, primary_key=True, serialize=False, verbose_name='ID')), 31 | ('title', models.CharField(max_length=150, verbose_name='عنوان')), 32 | ('description', models.TextField(verbose_name='توضیحات')), 33 | ('slug', models.SlugField(editable=False)), 34 | ('is_valid', models.CharField(choices=[('rejected', 'رد'), ('approved', 'تائید'), ('pending', 'در انتظار')], default='pending', max_length=20, verbose_name='اعتبارسنجی')), 35 | ('created_on', django_jalali.db.models.jDateTimeField(auto_now=True)), 36 | ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 37 | ('tags', models.ManyToManyField(to='tickets.tag', verbose_name='تگ ها')), 38 | ], 39 | ), 40 | migrations.CreateModel( 41 | name='Comment', 42 | fields=[ 43 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 44 | ('body', models.CharField(max_length=250, verbose_name='نظر')), 45 | ('created_on', django_jalali.db.models.jDateTimeField(auto_now=True)), 46 | ('created_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 47 | ('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tickets.ticket')), 48 | ], 49 | ), 50 | ] 51 | -------------------------------------------------------------------------------- /tickets/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/tickets/migrations/__init__.py -------------------------------------------------------------------------------- /tickets/models.py: -------------------------------------------------------------------------------- 1 | from accounts.models import User 2 | from django.db import models 3 | from django.urls import reverse 4 | from django.utils.text import slugify 5 | from django.utils.translation import gettext as _ 6 | from django_jalali.db import models as jmodels 7 | from main.utils import generateUID 8 | 9 | from .constants import VERIFICATIONS 10 | 11 | 12 | class Tag(models.Model): 13 | name = models.CharField( 14 | max_length=50, 15 | ) 16 | description = models.CharField( 17 | max_length=150, 18 | ) 19 | created_on = jmodels.jDateTimeField( 20 | auto_now=True, 21 | editable=False, 22 | ) 23 | 24 | def __str__(self): return self.name 25 | 26 | 27 | class Ticket(models.Model): 28 | id = models.CharField( 29 | verbose_name='ID', 30 | max_length=11, 31 | primary_key=True, 32 | editable=False, 33 | ) 34 | title = models.CharField( 35 | verbose_name=_('Title'), 36 | max_length=150, 37 | ) 38 | description = models.TextField( 39 | verbose_name=_('Description') 40 | ) 41 | tags = models.ManyToManyField( 42 | Tag, 43 | verbose_name=_('Tags'), 44 | ) 45 | slug = models.SlugField( 46 | editable=False, 47 | ) 48 | is_valid = models.CharField( 49 | verbose_name=_('Validation'), 50 | choices=VERIFICATIONS, 51 | default='pending', 52 | max_length=20, 53 | ) 54 | created_on = jmodels.jDateTimeField( 55 | auto_now=True, 56 | editable=False, 57 | ) 58 | created_by = models.ForeignKey( 59 | User, 60 | on_delete=models.CASCADE, 61 | # editable=False, 62 | null=True, 63 | ) 64 | 65 | # TODO: Adding a view counter using django-hitcount pkg 66 | 67 | def __str__(self): return self.title 68 | 69 | def save(self, *args, **kwargs): 70 | self.slug = slugify(self.title, allow_unicode=True) 71 | 72 | if not self.id: 73 | self.id = generateUID(Ticket) 74 | 75 | return super().save(*args, **kwargs) 76 | 77 | def get_absolute_url(self): 78 | return reverse('ticket', args=[str(self.id), str(self.slug)]) 79 | 80 | 81 | class Comment(models.Model): 82 | body = models.CharField( 83 | verbose_name=_('Comment Body'), 84 | max_length=250, 85 | ) 86 | ticket = models.ForeignKey( 87 | Ticket, 88 | on_delete=models.CASCADE, 89 | ) 90 | created_by = models.ForeignKey( 91 | User, 92 | on_delete=models.CASCADE, 93 | editable=False, 94 | null=True, 95 | ) 96 | created_on = jmodels.jDateTimeField( 97 | auto_now=True, 98 | editable=False, 99 | ) 100 | 101 | def __str__(self): 102 | return f'{self.created_by} on {self.ticket}' 103 | 104 | def get_absolute_url(self): 105 | return reverse('ticket', args=[str(self.ticket.id), str(self.ticket.slug)]) -------------------------------------------------------------------------------- /tickets/templates/create_ticket.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load crispy_forms_tags %} 3 | {% load static %} 4 | 5 | {% block meta_og_title %}ایجاد برچسب جدید{% endblock %} 6 | 7 | {% block meta_description %}یک برچسب جدید بسازید و خطا ها و اسکریپت هاتون رو به نمایش بذارید تا از نظرات دیگر کاربران 8 | باخبر شید!{% endblock %} 9 | 10 | {% block meta_og_description %}یک برچسب جدید بسازید و خطا ها و اسکریپت هاتون رو به نمایش بذارید تا از نظرات دیگر کاربران 11 | باخبر شید!{% endblock %} 12 | 13 | {% block meta_og_url %}{% url 'new_ticket' %}{% endblock %} 14 | 15 | {% block meta_tw_title %}ایجاد برچسب جدید{% endblock %} 16 | 17 | {% block meta_tw_description %}یک برچسب جدید بسازید و خطا ها و اسکریپت هاتون رو به نمایش بذارید تا از نظرات دیگر کاربران 18 | باخبر شید!{% endblock %} 19 | 20 | {% block meta_tw_url %}{% url 'new_ticket' %}{% endblock %} 21 | 22 | {% block ticket_active %}active{% endblock %} 23 | {% block title %}ایجاد برچسب جدید{% endblock %} 24 | 25 | {% block carousel_image %}{% static 'img/ticket-banner.png' %}{% endblock %} 26 | 27 | {% block content %} 28 | {% crispy form %} 29 | {% endblock %} -------------------------------------------------------------------------------- /tickets/templates/ticket.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load static %} 3 | {% load markdown_extras %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block meta_og_title %}{{ticket.title}}{% endblock %} 7 | 8 | {% block meta_description %}{{ticket.description}}{% endblock %} 9 | 10 | {% block meta_og_description %}{{ticket.description}}{% endblock %} 11 | 12 | {% block meta_og_url %}{{ticket.get_absolute_url}}{% endblock %} 13 | 14 | {% block meta_tw_title %}{{ticket.title}}{% endblock %} 15 | 16 | {% block meta_tw_description %}{{ticket.description}}{% endblock %} 17 | 18 | {% block meta_tw_url %}{{ticket.get_absolute_url}}{% endblock %} 19 | 20 | {% block title %}{{ticket.title}}{% endblock %} 21 | 22 | {% block content %} 23 |

{{ticket.title}}

24 |

نوشته شده توسط {{ticket.created_by}} در {{ticket.created_on|date:'d-m-Y'}}

25 | 26 |
27 | {{ticket.description|markdown|safe}} 28 |
29 | 30 | {% for tag in ticket.tags.all %} 31 | {{tag.name}}# 32 | {% endfor %} 33 |
34 | 35 | {% if comments %} 36 |
37 | {% for comment in comments %} 38 |
39 |
40 |

{{comment.body}}

41 |

نوشته شده توسط {{comment.created_by}} 42 | {{comment.created_on|date:'Y-m-d ‍ H:i'}} 43 |

44 |
45 |
46 | {% endfor %} 47 | {% endif %} 48 |
49 | {% if ticket.is_valid == 'pending' %} 50 |
51 |
52 |
برچسب ساخته شده، عمومی نیست!
53 |

برچسب شما در دست بررسی می باشد و تا زمان تایید شدن، تنها برای شما قابل نمایش است.

54 |
55 |
56 | {% elif ticket.is_valid == 'rejected' %} 57 |
58 |
59 |
برچسب ساخته شده، عمومی نیست!
60 |

متاسفانه برچسب ساخته شده توسط شما، قابلیت نمایش بصورت عمومی را ندارد.

61 |
62 |
63 | {% else %} 64 | {% if user.is_authenticated %} 65 |
66 | {% crispy form %} 67 | {% else %} 68 |
جهت ثبت نظر، ابتدا وارد شوید!
69 | {% endif %} 70 | {% endif %} 71 | {% endblock %} -------------------------------------------------------------------------------- /tickets/templates/ticket_search_results.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load static %} 3 | 4 | {% block meta_og_title %}نتیجه جستجو عبارت '{{searched_keyword}}' در برچسب ها..{% endblock %} 5 | 6 | {% block meta_description %}{{tickets.count}} برچسب مرتبط یافت شد!{% endblock %} 7 | 8 | {% block meta_og_description %}{{tickets.count}} برچسب مرتبط یافت شد!{% endblock %} 9 | 10 | {% block meta_og_url %}{% url 'ticket_search_results' %}?t={{searched_keyword}}{% endblock %} 11 | 12 | {% block meta_tw_title %}نتیجه جستجو عبارت '{{searched_keyword}}' در برچسب ها..{% endblock %} 13 | 14 | {% block meta_tw_description %}{{tickets.count}} برچسب مرتبط یافت شد!{% endblock %} 15 | 16 | {% block meta_tw_url %}{% url 'ticket_search_results' %}?t={{searched_keyword}}{% endblock %} 17 | 18 | {% block title %}نتیجه جستجو برای "{{searched_keyword}}"{% endblock %} 19 | 20 | {% block carousel_image %}{% static 'img/ticket-search-banner.png' %}{% endblock %} 21 | 22 | {% block content %} 23 |

نتیجه جستجو عبارت "{{searched_keyword}}" در برچسب ها..


24 | {% if tickets %} 25 | {% for ticket in tickets %} 26 |
27 |
28 | 29 |
{{ticket.title}}
30 |

31 | نوشته شده توسط {{ticket.created_by}}  32 | {% for tag in ticket.tags.all %} 33 | {{tag.name}}# 34 | {% endfor %} 35 | ثبت شده در {{ticket.created_on |date:'Y.m.d'}} 36 |

37 |
38 |
39 | {% endfor %} 40 | {% else %} 41 |

برچسبی یافت نشد!

42 | {% endif %} 43 | {% endblock %} -------------------------------------------------------------------------------- /tickets/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /tickets/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codehub-ir/codehub/1013325540351aadd00b13e2b873b4b768a53827/tickets/tests/__init__.py -------------------------------------------------------------------------------- /tickets/tests/test_models.py: -------------------------------------------------------------------------------- 1 | from accounts.models import User 2 | from django.test import TestCase 3 | from tickets.models import Comment, Tag, Ticket 4 | 5 | 6 | class TagTestCase(TestCase): 7 | 8 | @classmethod 9 | def setUpTestData(cls) -> None: 10 | cls.sample = { 11 | 'name': 'AngularLA', 12 | 'description': 'a framework built in LA', 13 | } 14 | 15 | def setUp(self) -> None: 16 | self.tag = Tag.objects.create(**self.sample) 17 | 18 | def test_if_obj_returns_same_fields(self): 19 | fields = { 20 | 'name': self.tag.name, 21 | 'description': self.tag.description, 22 | } 23 | 24 | self.assertEqual(fields, self.sample) 25 | 26 | 27 | class TicketTestCase(TestCase): 28 | 29 | @classmethod 30 | def setUpTestData(cls) -> None: 31 | cls.ticket_sample = { 32 | 'title': 'Ticket Title', 33 | 'description': 'Ticket Description', 34 | } 35 | 36 | cls.user_sample = { 37 | 'username': 'johndoe', 38 | 'display_name': 'John Doe', 39 | 'email': 'john@doe.com', 40 | 'password': 'testPass123', 41 | } 42 | 43 | cls.tag_sample = { 44 | 'name': 'Tag Name', 45 | 'description': 'Tag Description', 46 | } 47 | 48 | def setUp(self) -> None: 49 | _user = User.objects.create(**self.user_sample) 50 | 51 | ''' 52 | Since the Ticket.tags is a ManyToMany field, 53 | It needs to get saved immediately right after 54 | It created. 55 | ''' 56 | _tag = Tag(**self.tag_sample) 57 | _tag.save() 58 | 59 | self.ticket = Ticket.objects.create(**self.ticket_sample) 60 | self.ticket.save() 61 | 62 | ''' 63 | Then we add those pre-built Tag objects to 64 | the Ticket object tags field. -> Ticket.tags 65 | ''' 66 | self.ticket.tags.set([_tag]) 67 | self.ticket.created_by = _user 68 | 69 | def test_obj_returns_same_fields(self): 70 | fields = { 71 | 'title': self.ticket.title, 72 | 'description': self.ticket.description, 73 | } 74 | 75 | self.assertEqual(fields, self.ticket_sample) 76 | 77 | def test_check_ticket_creator(self): 78 | self.assertEqual(self.ticket.created_by.username, 79 | self.user_sample['username']) 80 | 81 | 82 | class CommentTestCase(TestCase): 83 | 84 | @classmethod 85 | def setUpTestData(cls) -> None: 86 | cls.ticket_sample = { 87 | 'title': 'Ticket Title', 88 | 'description': 'Ticket Description', 89 | } 90 | cls.user_sample = { 91 | 'username': 'johndoe', 92 | 'display_name': 'John Doe', 93 | 'email': 'john@doe.com', 94 | 'password': 'testPass123', 95 | } 96 | cls.comment_sample = { 97 | 'body': 'Body of the comment', 98 | } 99 | 100 | def setUp(self) -> None: 101 | self.ticket = Ticket.objects.create(**self.ticket_sample) 102 | self.user = User.objects.create(**self.user_sample) 103 | 104 | self.comment_sample['ticket'] = self.ticket 105 | self.comment_sample['created_by'] = self.user 106 | 107 | self.comment = Comment.objects.create( 108 | **self.comment_sample 109 | ) 110 | 111 | def test_if_obj_returns_same_fields(self): 112 | fields = { 113 | 'body': self.comment.body, 114 | 'ticket': self.comment.ticket, 115 | 'created_by': self.comment.created_by, 116 | } 117 | 118 | self.assertEqual(fields, self.comment_sample) 119 | 120 | def test_comment_user_authority(self): 121 | self.assertEqual(self.comment.created_by, self.user) 122 | -------------------------------------------------------------------------------- /tickets/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from .views import TicketCreateView, TicketSearchResultsView, TicketView 4 | 5 | urlpatterns = [ 6 | 7 | # Template URLs 8 | path('new-ticket/', TicketCreateView.as_view(), name='new_ticket'), 9 | path('ticket//', TicketView.as_view(), name='ticket'), 10 | path('search/', TicketSearchResultsView.as_view(), name='ticket_search_results'), 11 | ] 12 | -------------------------------------------------------------------------------- /tickets/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.mixins import LoginRequiredMixin 2 | from django.db.models import Q 3 | from django.http import Http404 4 | from django.shortcuts import redirect 5 | from django.urls import reverse 6 | from django.views.generic import CreateView, DetailView, ListView 7 | 8 | from .forms import CommentCreateForm, TicketCreateForm 9 | from .models import Comment, Ticket 10 | 11 | 12 | class TicketCreateView(LoginRequiredMixin, CreateView): 13 | template_name = 'create_ticket.html' 14 | model = Ticket 15 | form_class = TicketCreateForm 16 | 17 | def form_valid(self, form): 18 | user = self.request.user 19 | form.instance.created_by = user 20 | return super().form_valid(form) 21 | 22 | 23 | class TicketView(DetailView, CreateView): 24 | model = Ticket 25 | template_name = 'ticket.html' 26 | form_class = CommentCreateForm 27 | 28 | def get_context_data(self, **kwargs): 29 | ticket = kwargs['object'] 30 | if ticket.created_by == self.request.user or ticket.is_valid == 'approved': 31 | context = super(TicketView, self).get_context_data(**kwargs) 32 | context['comments'] = Comment.objects.filter( 33 | ticket=ticket).order_by('-created_on') 34 | return context 35 | else: 36 | raise Http404() 37 | 38 | def form_valid(self, form, **kwargs): 39 | user = self.request.user 40 | if user.is_authenticated: 41 | ticket = self.get_object() 42 | form.instance.ticket = ticket 43 | form.instance.created_by = user 44 | return super().form_valid(form) 45 | else: 46 | return redirect(reverse('login')) 47 | 48 | 49 | class TicketSearchResultsView(ListView): 50 | model = Ticket 51 | template_name = 'ticket_search_results.html' 52 | context_object_name = 'tickets' 53 | extra_context = { 54 | 'searched_keyword': None, 55 | } 56 | 57 | def get_queryset(self): 58 | key = self.request.GET.get('t') 59 | self.extra_context['searched_keyword'] = key 60 | 61 | tickets = Ticket.objects.filter( 62 | Q(title__icontains=key, 63 | is_valid='approved') 64 | ) 65 | return tickets 66 | 67 | def get_context_data(self, **kwargs): 68 | context = super(TicketSearchResultsView, 69 | self).get_context_data(**kwargs) 70 | context.update(self.extra_context) 71 | return context 72 | --------------------------------------------------------------------------------