├── .gitignore
├── .travis.yml
├── README.md
├── __init__.py
├── actions
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
├── models.py
├── tests.py
├── utils.py
└── views.py
├── api
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
├── blog
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20160514_0813.py
│ ├── 0003_auto_20161008_1137.py
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
├── comments
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20160514_0738.py
│ ├── 0003_auto_20160514_0738.py
│ ├── 0004_auto_20160528_0533.py
│ ├── 0005_auto_20161003_1730.py
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
├── conf
├── gunicorn_conf.py
└── supervisord.conf
├── constants
└── __init__.py
├── data
├── data.json
└── location.json
├── dev
├── commonJSFile
│ ├── ajax.js
│ ├── form.js
│ └── preview.js
├── css
│ ├── common.less
│ ├── food
│ │ ├── create.less
│ │ ├── detail.less
│ │ ├── explore.less
│ │ └── list.less
│ ├── home
│ │ └── index.less
│ ├── topic
│ │ ├── detail.less
│ │ └── list.less
│ └── user
│ │ ├── base.less
│ │ ├── food_list.less
│ │ ├── index.less
│ │ ├── list.less
│ │ ├── login_register.less
│ │ ├── profile.less
│ │ ├── settings.less
│ │ ├── share.less
│ │ └── topic_collection.less
├── js
│ ├── food
│ │ ├── create.js
│ │ ├── detail.js
│ │ ├── explore.js
│ │ └── list.js
│ ├── home
│ │ └── index.js
│ ├── topic
│ │ ├── detail.js
│ │ └── list.js
│ └── user
│ │ ├── base.js
│ │ ├── food_list.js
│ │ ├── index.js
│ │ ├── list.js
│ │ ├── login.js
│ │ ├── profile.js
│ │ ├── register.js
│ │ ├── settings.js
│ │ ├── share.js
│ │ └── topic_collection.js
└── templates
│ ├── food
│ ├── create.html
│ ├── detail.html
│ ├── explore.html
│ └── list.html
│ ├── home
│ └── index.html
│ ├── topic
│ ├── detail.html
│ └── list.html
│ └── user
│ ├── base.html
│ ├── food_list.html
│ ├── index.html
│ ├── list.html
│ ├── login.html
│ ├── profile.html
│ ├── register.html
│ ├── settings.html
│ ├── share.html
│ └── topic_collection.html
├── ext
└── __init__.py
├── fixtures
└── initial_data.json
├── food
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20160514_0738.py
│ ├── 0003_auto_20160515_0738.py
│ ├── 0004_auto_20160522_1229.py
│ ├── 0005_auto_20160522_1353.py
│ ├── 0006_auto_20160528_0511.py
│ ├── 0007_auto_20160604_0849.py
│ └── __init__.py
├── models.py
├── templates
│ └── food
│ │ ├── create.html
│ │ ├── create.tpl
│ │ ├── detail.html
│ │ ├── detail.tpl
│ │ ├── explore.html
│ │ ├── explore.tpl
│ │ ├── list.html
│ │ └── list.tpl
├── tests.py
├── urls.py
└── views.py
├── forum
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20161008_1137.py
│ └── __init__.py
├── models.py
├── templates
│ └── forum
│ │ ├── detail.tpl
│ │ └── index.tpl
├── tests.py
├── urls.py
└── views.py
├── home
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── templates
│ └── home
│ │ ├── base.tpl
│ │ ├── index.html
│ │ ├── index.tpl
│ │ └── navbar.tpl
├── tests.py
├── urls.py
└── views.py
├── location
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_place.py
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
├── manage.py
├── media
├── cache
│ ├── 20
│ │ └── 30
│ │ │ └── 203029d21c730d8b90087d5f7b586420.jpg
│ ├── 26
│ │ └── e5
│ │ │ └── 26e58558b4f30fd3608eea3b625255a2.jpg
│ ├── 38
│ │ └── 57
│ │ │ └── 3857eb185e17c4c4c80b10d1c573cd6f.jpg
│ ├── 57
│ │ └── ea
│ │ │ └── 57ea8394398cb375b9eb39181c1c2fb9.jpg
│ ├── 58
│ │ └── d9
│ │ │ └── 58d95a5917d42a0a7e85fb6b007e93ee.jpg
│ ├── 69
│ │ └── c8
│ │ │ └── 69c834a00be1a190b360d2e1a5104c81.jpg
│ ├── 70
│ │ └── 08
│ │ │ └── 700890c160c23fbe55b81af05cdff76d.jpg
│ ├── 74
│ │ └── b0
│ │ │ └── 74b098dd53b0b05ca6faac60cb3dcbb6.jpg
│ ├── 0e
│ │ ├── 35
│ │ │ └── 0e356957b375c31f151c91eadff99124.jpg
│ │ └── 9f
│ │ │ └── 0e9fd5ae5087c241754bb70837e94186.jpg
│ ├── 2a
│ │ └── 95
│ │ │ └── 2a95bc9e92a1bb2abb8465efb383a00c.jpg
│ ├── 3d
│ │ └── 5e
│ │ │ └── 3d5e42e6f394d5ce7c03bde326490b34.jpg
│ ├── 4d
│ │ └── bf
│ │ │ └── 4dbf961fd10ba4b0309ecbae8daa135e.jpg
│ ├── 6a
│ │ └── 47
│ │ │ └── 6a474beda343a724939a72b5dca05aa0.jpg
│ ├── 6d
│ │ └── 50
│ │ │ └── 6d50de93c73b2f41f09b409c75e44277.jpg
│ ├── 6f
│ │ └── 4d
│ │ │ └── 6f4d7d3d9c28a4dfa5d58d908c3a4d38.jpg
│ ├── 7d
│ │ └── 37
│ │ │ └── 7d372859d9202485c95f6782d118148a.jpg
│ ├── aa
│ │ └── 59
│ │ │ └── aa59a51567a07379fe4fef58513bce9d.jpg
│ ├── b0
│ │ └── 17
│ │ │ └── b017c6a13ffcf20d25e119f44543da24.jpg
│ ├── ba
│ │ └── 9a
│ │ │ └── ba9a8347cf2cf76131439915168c7302.jpg
│ ├── c5
│ │ └── f8
│ │ │ └── c5f8d4297de18fc4a2b70fe2ab92d39e.jpg
│ ├── d6
│ │ └── 9b
│ │ │ └── d69b7579b64edd8d840b90a1b03706fe.jpg
│ ├── da
│ │ └── 6a
│ │ │ └── da6a6363612d0a90fd6aa071281f9b60.jpg
│ ├── de
│ │ └── eb
│ │ │ └── deebf2d808d9329fcdde3e21a4f4507a.jpg
│ ├── e0
│ │ └── 52
│ │ │ └── e0528d9879ecb13a6757be825f26f306.jpg
│ ├── e2
│ │ └── f8
│ │ │ └── e2f80c38dd496f65d41964304e32de63.jpg
│ ├── f9
│ │ └── f1
│ │ │ └── f9f1a8adb2a51f18dd1efcc49f17ecb8.jpg
│ └── fa
│ │ └── cb
│ │ └── facb22872d24e58db5ca6c6b69269cdd.jpg
├── foods
│ └── cover
│ │ └── 2016
│ │ ├── 05
│ │ ├── 22
│ │ │ └── avatar.png
│ │ ├── 25
│ │ │ └── food3.jpg
│ │ └── 28
│ │ │ └── b219ebc4b74543a941fbf6af1c178a82b80114f4.jpg
│ │ └── 06
│ │ └── 05
│ │ ├── 543198769.jpg
│ │ ├── 543198769_YloxsaV.jpg
│ │ ├── 543198769_r9eurv5.jpg
│ │ ├── 543198769_z9W188q.jpg
│ │ └── boluobao.jpg
├── topic
│ └── cover
│ │ └── 2016
│ │ └── 05
│ │ └── 25
│ │ └── topic5.jpg
└── users
│ └── avatar
│ └── 2016
│ ├── 10
│ └── 06
│ │ └── 7101967.jpeg
│ └── 06
│ ├── 04
│ └── avatar.png
│ └── 07
│ └── avatar.png
├── message
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── tasks.py
├── tests.py
├── urls.py
└── views.py
├── package.json
├── pineapple
├── __init__.py
├── celery.py
├── management
│ ├── __init__.py
│ └── commands
│ │ ├── __init__.py
│ │ └── load_fixtures.py
├── settings.py
├── urls.py
├── views.py
└── wsgi.py
├── public
└── static
│ ├── assets
│ └── particles.json
│ ├── css
│ ├── common.css
│ ├── fonts
│ │ ├── icomoon.eot
│ │ ├── icomoon.svg
│ │ ├── icomoon.ttf
│ │ └── icomoon.woff
│ ├── food_create.css
│ ├── food_detail.css
│ ├── food_explore.css
│ ├── food_list.css
│ ├── forum_detail.css
│ ├── forum_list.css
│ ├── home_index.css
│ ├── topic_detail.css
│ ├── topic_list.css
│ ├── user_base.css
│ ├── user_food_list.css
│ ├── user_index.css
│ ├── user_list.css
│ ├── user_login.css
│ ├── user_profile.css
│ ├── user_register.css
│ ├── user_settings.css
│ ├── user_share.css
│ ├── user_topic_collection.css
│ ├── vendors.css
│ └── wangEditor.css
│ ├── font
│ └── font-awesome-4.5.0
│ │ ├── HELP-US-OUT.txt
│ │ ├── css
│ │ ├── font-awesome.css
│ │ └── font-awesome.min.css
│ │ ├── fonts
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
│ │ ├── less
│ │ ├── animated.less
│ │ ├── bordered-pulled.less
│ │ ├── core.less
│ │ ├── fixed-width.less
│ │ ├── font-awesome.less
│ │ ├── icons.less
│ │ ├── larger.less
│ │ ├── list.less
│ │ ├── mixins.less
│ │ ├── path.less
│ │ ├── rotated-flipped.less
│ │ ├── stacked.less
│ │ └── variables.less
│ │ └── scss
│ │ ├── _animated.scss
│ │ ├── _bordered-pulled.scss
│ │ ├── _core.scss
│ │ ├── _fixed-width.scss
│ │ ├── _icons.scss
│ │ ├── _larger.scss
│ │ ├── _list.scss
│ │ ├── _mixins.scss
│ │ ├── _path.scss
│ │ ├── _rotated-flipped.scss
│ │ ├── _stacked.scss
│ │ ├── _variables.scss
│ │ └── font-awesome.scss
│ ├── images
│ ├── anonymous.jpg
│ ├── back.png
│ ├── back2.jpg
│ ├── back3.jpg
│ ├── bg.jpg
│ ├── detail1.jpg
│ ├── details.jpg
│ ├── female.png
│ ├── food.jpg
│ ├── food2.jpg
│ ├── food3.jpg
│ ├── home-header-bg-563ac1688e.png
│ ├── male.png
│ ├── topic1.jpg
│ ├── topic2.jpg
│ ├── topic3.jpg
│ ├── topic4.jpg
│ ├── topic5.jpg
│ ├── topic6.jpg
│ ├── topic7.jpg
│ └── user.jpg
│ └── js
│ ├── chat.js
│ ├── csrf.js
│ ├── food_create.js
│ ├── food_detail.js
│ ├── food_explore.js
│ ├── food_list.js
│ ├── home_index.js
│ ├── js.cookie.js
│ ├── topic_detail.js
│ ├── topic_list.js
│ ├── user_base.js
│ ├── user_food_list.js
│ ├── user_index.js
│ ├── user_list.js
│ ├── user_login.js
│ ├── user_profile.js
│ ├── user_register.js
│ ├── user_settings.js
│ ├── user_share.js
│ ├── user_topic_collection.js
│ ├── vendors.js
│ └── wangEditor.js
├── requirements.txt
├── run_celery.sh
├── scripts
└── deploy.sh
├── search
├── context_processors.py
├── forms.py
├── index.py
├── templates
│ └── search
│ │ └── search.tpl
├── test.py
├── urls.py
└── views.py
├── topic
├── __init__.py
├── admin.py
├── apps.py
├── food_list.tpl
├── forms.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20160514_0738.py
│ ├── 0003_auto_20160515_0738.py
│ ├── 0004_foodtopic_total_collects.py
│ ├── 0005_foodtopic_cover_image.py
│ ├── 0006_foodtopic_description.py
│ └── __init__.py
├── models.py
├── templates
│ └── topic
│ │ ├── detail.html
│ │ ├── detail.tpl
│ │ ├── list.html
│ │ ├── list.tpl
│ │ └── list_ajax.tpl
├── tests.py
├── urls.py
└── views.py
├── user
├── __init__.py
├── admin.py
├── apps.py
├── auth.py
├── forms.py
├── middlewares.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20160513_0509.py
│ ├── 0003_usersetting_background_img.py
│ ├── 0004_auto_20160515_0738.py
│ ├── 0005_auto_20160604_0849.py
│ └── __init__.py
├── models.py
├── tasks.py
├── templates
│ └── user
│ │ ├── base.html
│ │ ├── base.tpl
│ │ ├── chat.tpl
│ │ ├── confirm.tpl
│ │ ├── food_list.html
│ │ ├── food_list.tpl
│ │ ├── index.html
│ │ ├── index.tpl
│ │ ├── list.html
│ │ ├── login.html
│ │ ├── login.tpl
│ │ ├── moments.tpl
│ │ ├── profile.html
│ │ ├── profile.tpl
│ │ ├── register.html
│ │ ├── register.tpl
│ │ ├── settings.html
│ │ ├── settings.tpl
│ │ ├── share.html
│ │ ├── share.tpl
│ │ ├── topic_collection.html
│ │ ├── topic_collection.tpl
│ │ ├── user_list.html
│ │ └── user_list.tpl
├── tests.py
├── urls.py
└── views.py
├── utils
├── __init__.py
├── decorators.py
├── form.py
├── mixins.py
├── taggit.py
├── templatetag
│ └── __init__.py
└── tests.py
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | venv/
2 | .DS_Store
3 | __pycache__/
4 | *.pyc
5 | .coverage
6 | /node_modules/
7 | *.hot-update.js
8 | *.hot-update.json
9 | public/static/js/main.js
10 | media/
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 |
3 | python:
4 | - "3.4"
5 |
6 | services:
7 | - mysql
8 |
9 | env:
10 | - DJANGO_VERSION=1.9.6
11 |
12 | before_install:
13 | - mysql -e "create database IF NOT EXISTS test;" -uroot -p mysql
14 |
15 | install:
16 | - pip install -r requirements.txt
17 |
18 | before_script:
19 | - mysql -e 'create database pineapple;'
20 | - python3 manage.py makemigrations
21 | - python3 manage.py migrate
22 |
23 | script:
24 | - python3 manage.py test
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pineapple
2 |
3 | 发现好吃的,好玩的🍍
4 |
5 | [演示地址](http://182.254.232.181/)
6 |
7 | ## intro
8 |
9 | pineapple是一个基于吃货分享的社区。
10 |
11 | 功能:
12 |
13 | - 分享有趣的食物
14 | - 浏览和评论美食专题和介绍
15 | - 轻量简约的社区和社交形式
16 |
17 | ## 部署
18 |
19 | 依赖环境
20 |
21 | - mysql
22 | - redis
23 | - python3.x(virtualenv)
24 |
25 | 初始化
26 |
27 | ```
28 | pip3 install requirements.txt
29 | python3 manager.py makemigrations
30 | python3 manager.py makemigrations thumbnail
31 | python3 manager.py migrate
32 | ```
33 |
34 | 测试运行
35 |
36 | ```
37 | python3 manager.py runserver
38 | ```
39 |
40 | ## demo
41 |
42 | 首页
43 |
44 | 
45 |
46 | 专题页
47 |
48 | 
49 |
50 | 专题内容
51 |
52 | 
53 |
54 | 发现页
55 |
56 | 
57 |
58 | 列表页
59 |
60 | 
61 |
62 | 详情页
63 |
64 | 
65 |
66 | 用户页
67 |
68 | 
69 |
70 | 私信功能
71 |
72 | 
73 |
74 | 讨论区
75 |
76 | 
77 |
78 | 评论页
79 |
80 | 
81 |
82 | 分享页
83 |
84 | 
85 |
86 | 注册页
87 |
88 | 
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/__init__.py
--------------------------------------------------------------------------------
/actions/__init__.py:
--------------------------------------------------------------------------------
1 | default_app_config = 'actions.apps.ActionsConfig'
--------------------------------------------------------------------------------
/actions/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from .models import Action
4 |
5 | # Register your models here.
6 | class ActionAdmin(admin.ModelAdmin):
7 | model = Action
8 |
9 |
10 | admin.site.register(Action, ActionAdmin)
11 |
--------------------------------------------------------------------------------
/actions/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ActionsConfig(AppConfig):
5 | name = 'actions'
6 | verbose_name = '活动'
7 |
--------------------------------------------------------------------------------
/actions/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-13 05:09
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | initial = True
13 |
14 | dependencies = [
15 | ('contenttypes', '0002_remove_content_type_name'),
16 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
17 | ]
18 |
19 | operations = [
20 | migrations.CreateModel(
21 | name='Action',
22 | fields=[
23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
24 | ('verb', models.CharField(max_length=255, verbose_name='活动描述')),
25 | ('target_id', models.PositiveIntegerField(blank=True, db_index=True, null=True, verbose_name='活动对象id')),
26 | ('created', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='活动时间')),
27 | ('target_ct', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='target_obj', to='contenttypes.ContentType', verbose_name='活动对象类型')),
28 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='actions', to=settings.AUTH_USER_MODEL, verbose_name='用户')),
29 | ],
30 | options={
31 | 'verbose_name': '活动',
32 | 'ordering': ('-created',),
33 | 'verbose_name_plural': '活动描述',
34 | },
35 | ),
36 | ]
37 |
--------------------------------------------------------------------------------
/actions/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/actions/migrations/__init__.py
--------------------------------------------------------------------------------
/actions/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.contenttypes.models import ContentType
3 | from django.contrib.contenttypes.fields import GenericForeignKey
4 | from constants import *
5 |
6 | from user.models import User
7 |
8 |
9 | action_classes = {
10 | SHARE: 'fa-share-alt',
11 | LIKE: 'fa-thumbs-up',
12 | COLLECT: 'fa-bookmark',
13 | FOLLOW: 'fa-eye',
14 | COMMENT: 'fa-comment',
15 | WTA: 'fa-cutlery',
16 | ATE: 'fa-hand-peace-o',
17 | POST: 'fa-send'
18 | }
19 |
20 | # Create your models here.
21 | class Action(models.Model):
22 | user = models.ForeignKey(User,
23 | related_name='actions',
24 | db_index=True, verbose_name='用户')
25 | verb = models.CharField(max_length=255, verbose_name='活动描述')
26 | target_ct = models.ForeignKey(ContentType,
27 | blank=True,
28 | null=True,
29 | related_name='target_obj', verbose_name='活动对象类型')
30 | target_id = models.PositiveIntegerField(null=True,
31 | blank=True,
32 | db_index=True, verbose_name='活动对象id')
33 | target = GenericForeignKey('target_ct', 'target_id')
34 | created = models.DateTimeField(auto_now_add=True,
35 | db_index=True, verbose_name='活动时间')
36 |
37 | def __str__(self):
38 | return self.user.username + self.verb + self.target_ct.app_label
39 |
40 | def get_action_class(self):
41 | return action_classes.get(self.verb, "")
42 |
43 | class Meta:
44 | ordering = ('-created',)
45 | verbose_name = '活动'
46 | verbose_name_plural = '活动描述'
--------------------------------------------------------------------------------
/actions/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/actions/utils.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from django.utils import timezone
3 | from django.contrib.contenttypes.models import ContentType
4 | from .models import Action
5 |
6 |
7 | def create_action(user, verb, target=None):
8 | now = timezone.now()
9 | last_minute = now - datetime.timedelta(seconds=120)
10 | similar_actions = Action.objects.filter(user_id=user.id,
11 | verb=verb,
12 | created__gte=last_minute)
13 | if target:
14 | target_ct = ContentType.objects.get_for_model(target)
15 | similar_actions = similar_actions.filter(target_ct=target_ct,
16 | target_id=target.id)
17 |
18 | if not similar_actions:
19 | action = Action(user=user, verb=verb, target=target)
20 | action.save()
21 | return True
22 | return False
23 |
--------------------------------------------------------------------------------
/actions/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/api/__init__.py
--------------------------------------------------------------------------------
/api/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/api/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ApiConfig(AppConfig):
5 | name = 'api'
6 |
--------------------------------------------------------------------------------
/api/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/api/migrations/__init__.py
--------------------------------------------------------------------------------
/api/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/api/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/api/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/blog/__init__.py:
--------------------------------------------------------------------------------
1 | default_app_config = 'blog.apps.BlogConfig'
--------------------------------------------------------------------------------
/blog/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from comments.models import PostComment
4 | from .models import Post
5 |
6 | # Register your models here.
7 | class PostCommentAdmin(admin.TabularInline):
8 | model = PostComment
9 |
10 |
11 | class PostAdmin(admin.ModelAdmin):
12 | model = Post
13 |
14 | inlines = [PostCommentAdmin]
15 |
16 |
17 | admin.site.register(Post, PostAdmin)
18 |
--------------------------------------------------------------------------------
/blog/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class BlogConfig(AppConfig):
5 | name = 'blog'
6 | verbose_name = '博客'
--------------------------------------------------------------------------------
/blog/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-14 07:38
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | initial = True
13 |
14 | dependencies = [
15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='Post',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('title', models.CharField(max_length=128, verbose_name='标题')),
24 | ('body', models.TextField(verbose_name='内容')),
25 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='创建日期')),
26 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='posts', to=settings.AUTH_USER_MODEL, verbose_name='用户')),
27 | ],
28 | options={
29 | 'verbose_name_plural': '博客',
30 | 'verbose_name': '博客',
31 | },
32 | ),
33 | ]
34 |
--------------------------------------------------------------------------------
/blog/migrations/0002_auto_20160514_0813.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-14 08:13
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('blog', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterModelOptions(
16 | name='post',
17 | options={'verbose_name': '帖子', 'verbose_name_plural': '帖子'},
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/blog/migrations/0003_auto_20161008_1137.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-10-08 03:37
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('blog', '0002_auto_20160514_0813'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterModelOptions(
16 | name='post',
17 | options={'verbose_name': '博客', 'verbose_name_plural': '博客'},
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/blog/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/blog/migrations/__init__.py
--------------------------------------------------------------------------------
/blog/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | from user.models import User
4 |
5 | # Create your models here.
6 | class Post(models.Model):
7 | title = models.CharField(max_length=128, verbose_name='标题')
8 | user = models.ForeignKey(User, related_name='posts', verbose_name='用户')
9 | body = models.TextField(verbose_name='内容')
10 | created = models.DateTimeField(auto_now_add=True, verbose_name='创建日期')
11 |
12 | class Meta:
13 | verbose_name = '博客'
14 | verbose_name_plural = '博客'
15 |
--------------------------------------------------------------------------------
/blog/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/blog/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/comments/__init__.py:
--------------------------------------------------------------------------------
1 | default_app_config = 'comments.apps.CommentsConfig'
--------------------------------------------------------------------------------
/comments/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from .models import FoodComment
4 |
5 |
6 | class FoodCommentAdmin(admin.ModelAdmin):
7 | model = FoodComment
8 |
9 |
10 | admin.site.register(FoodComment)
11 |
--------------------------------------------------------------------------------
/comments/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class CommentsConfig(AppConfig):
5 | name = 'comments'
6 | verbose_name = '评论'
--------------------------------------------------------------------------------
/comments/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 | from .models import FoodComment, ForumPostComment
4 | from utils.mixins import CleanContentMixin
5 |
6 | class FoodCommentForm(forms.ModelForm):
7 |
8 | class Meta:
9 | model = FoodComment
10 | fields = ('content',)
11 |
12 | class ForumPostCommentForm(forms.ModelForm, CleanContentMixin):
13 |
14 | class Meta:
15 | model = ForumPostComment
16 | fields = ('content',)
--------------------------------------------------------------------------------
/comments/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-13 05:09
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | initial = True
13 |
14 | dependencies = [
15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='Comment',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('content', models.CharField(max_length=512, verbose_name='评论内容')),
24 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='评论时间')),
25 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='评论用户')),
26 | ],
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/comments/migrations/0002_auto_20160514_0738.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-14 07:38
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('comments', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='FoodComment',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('content', models.CharField(max_length=512, verbose_name='评论内容')),
20 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='评论时间')),
21 | ],
22 | options={
23 | 'verbose_name_plural': '美食评论',
24 | 'verbose_name': '美食评论',
25 | },
26 | ),
27 | migrations.CreateModel(
28 | name='PostComment',
29 | fields=[
30 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
31 | ('content', models.CharField(max_length=512, verbose_name='评论内容')),
32 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='评论时间')),
33 | ],
34 | options={
35 | 'verbose_name_plural': '帖子评论',
36 | 'verbose_name': '帖子评论',
37 | },
38 | ),
39 | migrations.CreateModel(
40 | name='TopicComment',
41 | fields=[
42 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
43 | ('content', models.CharField(max_length=512, verbose_name='评论内容')),
44 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='评论时间')),
45 | ],
46 | options={
47 | 'verbose_name_plural': '专题评论',
48 | 'verbose_name': '专题评论',
49 | },
50 | ),
51 | migrations.RemoveField(
52 | model_name='comment',
53 | name='user',
54 | ),
55 | ]
56 |
--------------------------------------------------------------------------------
/comments/migrations/0003_auto_20160514_0738.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-14 07:38
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ('topic', '0002_auto_20160514_0738'),
14 | ('food', '0002_auto_20160514_0738'),
15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 | ('blog', '0001_initial'),
17 | ('comments', '0002_auto_20160514_0738'),
18 | ]
19 |
20 | operations = [
21 | migrations.DeleteModel(
22 | name='Comment',
23 | ),
24 | migrations.AddField(
25 | model_name='topiccomment',
26 | name='topic',
27 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='topic.FoodTopic', verbose_name='专题'),
28 | ),
29 | migrations.AddField(
30 | model_name='topiccomment',
31 | name='user',
32 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='评论用户'),
33 | ),
34 | migrations.AddField(
35 | model_name='postcomment',
36 | name='post',
37 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='blog.Post', verbose_name='帖子'),
38 | ),
39 | migrations.AddField(
40 | model_name='postcomment',
41 | name='user',
42 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='评论用户'),
43 | ),
44 | migrations.AddField(
45 | model_name='foodcomment',
46 | name='food',
47 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='food.Food', verbose_name='美食'),
48 | ),
49 | migrations.AddField(
50 | model_name='foodcomment',
51 | name='user',
52 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='评论用户'),
53 | ),
54 | ]
55 |
--------------------------------------------------------------------------------
/comments/migrations/0004_auto_20160528_0533.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-28 05:33
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('comments', '0003_auto_20160514_0738'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='foodcomment',
17 | name='content',
18 | field=models.TextField(max_length=512, verbose_name='评论内容'),
19 | ),
20 | migrations.AlterField(
21 | model_name='postcomment',
22 | name='content',
23 | field=models.TextField(max_length=512, verbose_name='评论内容'),
24 | ),
25 | migrations.AlterField(
26 | model_name='topiccomment',
27 | name='content',
28 | field=models.TextField(max_length=512, verbose_name='评论内容'),
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/comments/migrations/0005_auto_20161003_1730.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-10-03 09:30
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ('forum', '0001_initial'),
14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15 | ('comments', '0004_auto_20160528_0533'),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='ForumPostComment',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('content', models.TextField(max_length=512, verbose_name='评论内容')),
24 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='评论时间')),
25 | ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='forum.ForumPost', verbose_name='帖子')),
26 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='评论用户')),
27 | ],
28 | options={
29 | 'verbose_name': '帖子评论',
30 | 'verbose_name_plural': '帖子评论',
31 | },
32 | ),
33 | migrations.AlterModelOptions(
34 | name='postcomment',
35 | options={'verbose_name': '博客评论', 'verbose_name_plural': '博客评论'},
36 | ),
37 | ]
38 |
--------------------------------------------------------------------------------
/comments/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/comments/migrations/__init__.py
--------------------------------------------------------------------------------
/comments/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | from forum.models import ForumPost
4 | from blog.models import Post as BlogPost
5 | from food.models import Food
6 | from topic.models import FoodTopic
7 | from user.models import User
8 |
9 | # Create your models here.
10 | class Comment(models.Model):
11 | user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='评论用户')
12 | content = models.TextField(max_length=512, verbose_name='评论内容')
13 | created = models.DateTimeField(auto_now_add=True, verbose_name='评论时间')
14 |
15 | def __str__(self):
16 | return "{}: {}".format(self.user, self.content)
17 |
18 | class Meta:
19 | abstract = True
20 | verbose_name = '评论'
21 | verbose_name_plural = '评论'
22 |
23 |
24 | class FoodComment(Comment):
25 | food = models.ForeignKey(Food, on_delete=models.CASCADE, related_name='comments', verbose_name='美食')
26 |
27 | class Meta:
28 | verbose_name = '美食评论'
29 | verbose_name_plural = '美食评论'
30 |
31 |
32 | class TopicComment(Comment):
33 | topic = models.ForeignKey(FoodTopic, on_delete=models.CASCADE, related_name='comments', verbose_name='专题')
34 |
35 | class Meta:
36 | verbose_name = '专题评论'
37 | verbose_name_plural = '专题评论'
38 |
39 |
40 | class ForumPostComment(Comment):
41 | post = models.ForeignKey(ForumPost, on_delete=models.CASCADE, related_name='comments', verbose_name='帖子')
42 |
43 | class Meta:
44 | verbose_name = '帖子评论'
45 | verbose_name_plural = '帖子评论'
46 |
47 |
48 | class PostComment(Comment):
49 | post = models.ForeignKey(BlogPost, on_delete=models.CASCADE, related_name='comments', verbose_name='帖子')
50 |
51 | class Meta:
52 | verbose_name = '博客评论'
53 | verbose_name_plural = '博客评论'
54 |
--------------------------------------------------------------------------------
/comments/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/comments/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/conf/gunicorn_conf.py:
--------------------------------------------------------------------------------
1 | bind = '0.0.0.0:8000'
2 | backlog = 2048
3 |
4 | workers = 1
5 | worker_class = 'sync'
6 | worker_connections = 1000
7 | timeout = 30
8 | keepalive = 2
9 |
10 | daemon = False
11 | pidfile = None
12 | umask = 0
13 | user = None
14 | group = None
15 | tmp_upload_dir = None
16 |
17 | errorlog = '-'
18 | loglevel = 'info'
19 | accesslog = '-'
20 |
21 | proc_name = None
22 |
23 | def post_fork(server, worker):
24 | server.log.info("Worker spawned (pid: %s)", worker.pid)
25 |
26 | def pre_fork(server, worker):
27 | pass
28 |
29 | def pre_exec(server):
30 | server.log.info("Forked child, re-executing.")
31 |
32 | def when_ready(server):
33 | server.log.info("Server is ready. Spawning workers")
34 |
35 | def worker_int(worker):
36 | worker.log.info("worker received INT or QUIT signal")
37 |
38 | ## get traceback info
39 | import threading, sys, traceback
40 | id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
41 | code = []
42 | for threadId, stack in sys._current_frames().items():
43 | code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""),
44 | threadId))
45 | for filename, lineno, name, line in traceback.extract_stack(stack):
46 | code.append('File: "%s", line %d, in %s' % (filename,
47 | lineno, name))
48 | if line:
49 | code.append(" %s" % (line.strip()))
50 | worker.log.debug("\n".join(code))
51 |
52 | def worker_abort(worker):
53 | worker.log.info("worker received SIGABRT signal")
54 |
--------------------------------------------------------------------------------
/conf/supervisord.conf:
--------------------------------------------------------------------------------
1 | [program:pineapple]
2 | command=venv/bin/gunicorn pineapple.wsgi:application -c conf/gunicorn_conf.py
3 | directory=/code/pineapple
4 | user=root
5 | stdout_logfile=/var/log/supervisor/pineapple.log
6 | stderr_logfile=/var/log/supervisor/pineapple_err.log
7 | auto_start=True
8 | auto_restart=True
9 |
10 | [supervisord]
11 |
12 | [supervisorctl]
13 | serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
14 | serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
15 |
--------------------------------------------------------------------------------
/constants/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | COMMENT_SUCCESS = '评论成功'
3 | COMMENT_FAIL = '评论失败'
4 | COMMENT_AFTER_LOGIN = '请登录后评论'
5 | POST_ALREADY_LIKE = '请勿重复推荐'
6 | SETTING_UPDATE_SUCCESS = '设置更新成功'
7 | SETTING_UPDATE_FAIL = '设置更新失败'
8 | PROFILE_UPDATE_SUCCESS = '资料更新成功'
9 | PROFILE_UPDATE_FAIL = '资料更新失败'
10 | MAX_MESSAGE_LENGTH_REACH = '消息长度最大为200'
11 | MAX_CONTACT_COUNT_REACH = '超出最大联系人长度'
12 | INVALID_TIMESTAMP = '非法时间戳'
13 |
14 | SHARE = '分享了'
15 | POST = '发表了'
16 | LIKE = '喜欢了'
17 | WTA = '想吃'
18 | ATE = '吃过'
19 | FOLLOW = '关注了'
20 | COLLECT = '收藏了'
21 | COMMENT = '评论了'
22 |
23 | REDIS_FOOD_VIEWS_KEY = 'food:{}:views'
24 | REDIS_FOOD_RANKING_KEY = 'food_ranking'
25 | REDIS_MESSAGES_KEY = 'message:{}:store'
26 | REDIS_MESSAGES_UNREAD_KEY = 'message:{}:unread'
27 | REDIS_MESSAGE_USERS_KEY = 'message:users'
28 | REDIS_CONTACTS_KEY = 'contacts:{}:store'
29 | REDIS_POSTS_KEY = 'posts:{}:store'
30 |
31 | STATUS_INVALID_ARGUMENTS = '无效参数'
32 |
33 | JSON_SUCCESS = {'status': True}
34 | JSON_SUCCESS_WITH_DATA = lambda data: dict(data, **JSON_SUCCESS)
35 | JSON_FAIL = lambda reason='':{'status':False, 'reason': reason}
36 |
37 | MAX_MESSAGE_LENGTH = 200
38 | MAX_MESSAGES_COUNT = 100
39 | MESSAGES_TIMEOUT = 3600 * 24 * 7 # 私信缓存一周
40 | MAX_CONTACT_COUNT = 20
41 | POSTS_CACHE_TIME = 30 # 30秒更新一次
42 | MAX_HOT_DISPLAY_COUNT = 20
--------------------------------------------------------------------------------
/dev/commonJSFile/form.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | var loginContainer = document.getElementById("form-container");
3 | var inputs = loginContainer.getElementsByTagName("input")
4 |
5 | for(var idx=0;idx';
10 | }
11 | reader.readAsDataURL(file.files[0]);
12 | }
13 | else
14 | {
15 | prevDiv.innerHTML = '
';
16 | }
17 | }
18 |
19 | document.getElementById("preview-input").addEventListener("change", preview, false)
20 | };
21 |
--------------------------------------------------------------------------------
/dev/css/topic/list.less:
--------------------------------------------------------------------------------
1 | @baseColor: rgb(64,163,194);
2 |
3 | .topic-container {
4 | margin-top: 80px;
5 | }
6 |
7 | html body .navbar {
8 | background-color: @baseColor !important;
9 |
10 | .cursor {
11 | background-color: white !important;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/dev/css/user/food_list.less:
--------------------------------------------------------------------------------
1 | @baseFontColor: #5d5d5d;
2 | @baseFontLightColor: #828caa;
3 | @baseFontFamily: Lato, "PingFang SC", "Microsoft YaHei", sans-serif;
4 |
5 | .tab-container {
6 |
7 | @media screen and (max-width: 1150px) {
8 | .eat-container {
9 | width: 748px !important;
10 | }
11 | }
12 |
13 | .eat-container {
14 | margin: 30px auto;
15 | width: 1122px;
16 |
17 | .eat-item {
18 | width: 334px;
19 | float: left;
20 | padding: 5px;
21 | margin: 20px;
22 | background-color: white;
23 | box-shadow: 0 0 3px rgb(207, 207, 207);
24 |
25 | .food-image {
26 | float: left;
27 | width: 80px;
28 | height: 80px;
29 | margin-right: 10px;
30 | background-position: center;
31 | background-repeat: no-repeat;
32 | background-size: cover;
33 | }
34 |
35 | .eat-item-main {
36 | float: left;
37 | width: 230px;
38 |
39 |
40 | .food-title {
41 | margin: 0;
42 | margin-bottom: 5px;
43 | -o-text-overflow: ellipsis;
44 | text-overflow: ellipsis;
45 | overflow: hidden;
46 | white-space: nowrap;
47 | color: @baseFontColor;
48 | font-weight: 400;
49 |
50 | a {
51 | color: inherit;
52 | text-decoration: none;
53 | }
54 | }
55 |
56 | .food-desc {
57 | margin: 0;
58 | -o-text-overflow: ellipsis;
59 | text-overflow: ellipsis;
60 | overflow: hidden;
61 | white-space: nowrap;
62 | color: @baseFontLightColor;
63 | }
64 |
65 | .eat-item-icons {
66 | margin-top: 15px;
67 | margin-right: 5px;
68 | float: right;
69 |
70 | .fa, a {
71 | color: @baseFontLightColor;
72 | margin-left: 5px;
73 | vertical-align: bottom;
74 |
75 | .num {
76 | margin-left: 3px;
77 | font-family: @baseFontFamily;
78 | }
79 | }
80 | }
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/dev/css/user/list.less:
--------------------------------------------------------------------------------
1 | @imagesDir: "/public/static/images";
2 | @baseFontColor: #5d5d5d;
3 | @baseFontLightColor: #828caa;
4 | @baseFontFamily: Lato, "PingFang SC", "Microsoft YaHei", sans-serif;
5 |
6 | a {
7 | color: inherit;
8 | text-decoration: none;
9 | }
10 |
11 | .tab-container {
12 |
13 | @media screen and (max-width: 1335px) {
14 | .user-item-container {
15 | max-width: 1144px !important;
16 | }
17 | }
18 |
19 | @media screen and (max-width: 1160px) {
20 | .user-item-container {
21 | max-width: 970px !important;
22 | }
23 | }
24 |
25 | .user-item-container {
26 | margin: 0 auto;
27 | padding: 20px 50px;
28 | max-width: 1318px;
29 |
30 | .user-item {
31 | float: left;
32 | width: 150px;
33 | background-color: white;
34 | text-align: center;
35 | box-shadow: 0 0 3px rgb(207, 207, 207);
36 | margin: 12px;
37 |
38 | .user-item-portrait {
39 | width: 65px;
40 | height: 65px;
41 | border-radius: 50%;
42 | margin: 30px auto 20px auto;
43 | background-image: url("@{imagesDir}/anonymous.jpg");
44 | background-repeat: no-repeat;
45 | background-size: cover;
46 | background-position: center;
47 | }
48 |
49 | .user-item-info {
50 | display: block;
51 | color: @baseFontLightColor;
52 | margin-bottom: 5px;
53 | }
54 |
55 | .user-item-follow-btn {
56 | width: 100%;
57 | height: 40px;
58 | margin-top: 20px;
59 | border: none;
60 | border-top: 1px solid rgb(226, 226, 226);
61 | background-color: white;
62 | color: @baseFontColor;
63 | font-family: @baseFontFamily;
64 | font-size: 1.5rem;
65 | outline: none;
66 | cursor: pointer;
67 | }
68 |
69 | .has-follow {
70 | color: @baseFontLightColor !important;
71 | }
72 |
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/dev/css/user/profile.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/dev/css/user/profile.less
--------------------------------------------------------------------------------
/dev/css/user/share.less:
--------------------------------------------------------------------------------
1 | @baseFontColor: #5d5d5d;
2 | @baseFontLightColor: #828caa;
3 | @baseColor: rgb(122, 167, 242);
4 | @baseDeepColor: rgb(88, 138, 218);
5 | @imagesDir: "/public/static/images";
6 |
7 | body {
8 | min-width: 1240px;
9 | }
10 |
11 | .tab-container {
12 | .share-container {
13 | margin: 0 150px;
14 |
15 | .share-container-ul {
16 | .share-container-item {
17 | padding: 10px 0;
18 | list-style-type: none;
19 | border-bottom: 1px solid rgb(231, 231, 231);
20 |
21 | .share-time {
22 | float: right;
23 | color: @baseFontLightColor;
24 | margin-top: 16px;
25 | }
26 |
27 | .item-index {
28 | display: inline-block;
29 | margin-right: 15px;
30 | vertical-align: top;
31 | margin-top: 16px;
32 | font-size: 1.5rem;
33 | font-weight: bold;
34 | color: @baseFontLightColor;
35 | }
36 |
37 | .images-item {
38 | display: inline-block;
39 | width: 70px;
40 | height: 70px;
41 | margin-right: 20px;
42 | background-image: url("@{imagesDir}/topic1.jpg");
43 | background-position: center;
44 | background-repeat: no-repeat;
45 | background-size: cover;
46 | }
47 |
48 | .item-main {
49 | display: inline-block;
50 |
51 | .item-title {
52 | margin: 16px 0;
53 | color: @baseColor;
54 | font-size: 1.5rem;
55 | font-weight: 400;
56 |
57 | a {
58 | color: inherit;
59 | text-decoration: none;
60 |
61 | &:hover {
62 | color: @baseDeepColor;
63 | }
64 | }
65 | }
66 |
67 | .item-desc {
68 | margin-bottom: 0;
69 | -o-text-overflow: ellipsis;
70 | text-overflow: ellipsis;
71 | overflow: hidden;
72 | white-space: nowrap;
73 | width: 700px;
74 | color: @baseFontLightColor;
75 | font-weight: normal;
76 | }
77 | }
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/dev/css/user/topic_collection.less:
--------------------------------------------------------------------------------
1 | @baseFontColor: #5d5d5d;
2 | @baseFontLightColor: #828caa;
3 | @baseFontLighterColor: rgb(191, 191, 191);
4 | @baseFontFamily: Lato, "PingFang SC", "Microsoft YaHei", sans-serif;
5 | @baseColor: rgb(122, 167, 242);
6 |
7 | @media screen and (max-width: 1150px) {
8 | .topic-container {
9 | width: 840px !important;
10 | }
11 | }
12 |
13 | .tab-container {
14 | .topic-container {
15 | margin: 30px auto;
16 | width: 1120px;
17 |
18 | .topic-item {
19 | margin: 0 20px 20px 20px;
20 | width: 240px;
21 | padding-bottom: 10px;
22 | background-color: white;
23 | box-shadow: 0 0 3px rgb(207, 207, 207);
24 | color: @baseFontLighterColor;
25 | float: left;
26 |
27 | .topic-item-image-container {
28 | width: 220px;
29 | height: 130px;
30 | margin: 10px auto;
31 | overflow: hidden;
32 |
33 | .topic-item-image {
34 | width: 220px;
35 | height: 130px;
36 | background-position: center;
37 | background-repeat: no-repeat;
38 | background-size: cover;
39 | transition: transform .3s ease;
40 |
41 | &:hover {
42 | transform: scale(1.1, 1.1);
43 | }
44 | }
45 | }
46 |
47 |
48 | .collect-time {
49 | float: left;
50 | margin-left: 10px;
51 | }
52 |
53 | .topic-item-title {
54 | font-weight: normal;
55 | -o-text-overflow: ellipsis;
56 | text-overflow: ellipsis;
57 | overflow: hidden;
58 | white-space: nowrap;
59 | color: @baseFontLightColor;
60 | width: 100%;
61 | padding: 0 10px;
62 | margin-top: 0;
63 | margin-bottom: 0;
64 | text-align: center;
65 |
66 | a {
67 | color: inherit;
68 | text-decoration: none;
69 | }
70 | }
71 |
72 | .topic-item-icons {
73 | margin-right: 20px;
74 | text-align: center;
75 | font-size: 1.5rem;
76 | color: @baseFontLightColor;
77 |
78 | .fa {
79 | margin-left: 5px;
80 | .num {
81 | margin-left: 10px;
82 | font-family: @baseFontFamily;
83 | }
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/dev/js/food/detail.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/food/detail.less");
3 | window.$ajax = require("../../commonJSFile/ajax.js");
4 |
--------------------------------------------------------------------------------
/dev/js/food/explore.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/food/explore.less");
3 | window.$ajax = require("../../commonJSFile/ajax.js");
4 |
--------------------------------------------------------------------------------
/dev/js/food/list.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/food/list.less");
3 | window.$ajax = require("../../commonJSFile/ajax.js");
4 |
--------------------------------------------------------------------------------
/dev/js/home/index.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/home/index.less");
3 | window.$ajax = require("../../commonJSFile/ajax.js");
4 | // window.url = "https://api.unsplash.com/photos/random";
5 | // "fa60305aa82e74134cabc7093ef54c8e2c370c47e73152f72371c828daedfcd7"
6 |
7 | (function() {
8 | var container = document.getElementById("container");
9 | var clientHeight = document.documentElement.clientHeight;
10 | var first = true;
11 | container.style.height = clientHeight + "px";
12 |
13 | document.addEventListener("scroll", function() {
14 | if(document.body.scrollTop > clientHeight / 2 && first) {
15 | first = false;
16 | document.getElementById("navbar").className += " over-navbar";
17 | }else if(document.body.scrollTop < clientHeight / 2 - 1 && !first) {
18 | first = true;
19 | document.getElementById("navbar").className = "navbar";
20 | }
21 | }, false);
22 | })();
23 |
--------------------------------------------------------------------------------
/dev/js/topic/detail.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/topic/detail.less");
3 | window.$ajax = require("../../commonJSFile/ajax.js");
4 |
--------------------------------------------------------------------------------
/dev/js/topic/list.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/home/index.less");
3 | require("../../css/topic/list.less");
4 | window.$ajax = require("../../commonJSFile/ajax.js");
5 |
--------------------------------------------------------------------------------
/dev/js/user/base.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/user/base.less");
3 | window.$ajax = require("../../commonJSFile/ajax.js");
4 |
--------------------------------------------------------------------------------
/dev/js/user/food_list.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/user/base.less");
3 | require("../../css/user/food_list.less");
4 | window.$ajax = require("../../commonJSFile/ajax.js");
5 |
--------------------------------------------------------------------------------
/dev/js/user/index.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/user/base.less");
3 | require("../../css/user/index.less");
4 | window.$ajax = require("../../commonJSFile/ajax.js");
5 |
--------------------------------------------------------------------------------
/dev/js/user/list.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/user/base.less");
3 | require("../../css/user/list.less");
4 | window.$ajax = require("../../commonJSFile/ajax.js");
5 |
--------------------------------------------------------------------------------
/dev/js/user/login.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/user/login_register.less");
3 | var formAnimate = require("../../commonJSFile/form.js");
4 | formAnimate();
5 | window.$ajax = require("../../commonJSFile/ajax.js");
6 |
--------------------------------------------------------------------------------
/dev/js/user/profile.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/user/base.less");
3 | require("../../css/user/settings.less");
4 | require("../../commonJSFile/preview.js")();
5 | window.$ajax = require("../../commonJSFile/ajax.js");
6 |
--------------------------------------------------------------------------------
/dev/js/user/register.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/user/login_register.less");
3 | var formAnimate = require("../../commonJSFile/form.js");
4 | formAnimate();
5 | window.$ajax = require("../../commonJSFile/ajax.js");
6 |
--------------------------------------------------------------------------------
/dev/js/user/settings.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/user/base.less");
3 | require("../../css/user/settings.less");
4 | require("../../commonJSFile/preview.js")();
5 | window.$ajax = require("../../commonJSFile/ajax.js");
6 |
--------------------------------------------------------------------------------
/dev/js/user/share.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/user/base.less");
3 | require("../../css/user/share.less");
4 | window.$ajax = require("../../commonJSFile/ajax.js");
5 |
--------------------------------------------------------------------------------
/dev/js/user/topic_collection.js:
--------------------------------------------------------------------------------
1 | require("../../css/common.less");
2 | require("../../css/user/base.less");
3 | require("../../css/user/topic_collection.less");
4 | window.$ajax = require("../../commonJSFile/ajax.js");
5 |
--------------------------------------------------------------------------------
/dev/templates/user/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 登录
6 |
7 |
8 |
9 |
10 |
11 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
登录
33 | 登录,寻找更好的
34 |
35 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/ext/__init__.py:
--------------------------------------------------------------------------------
1 | import redis
2 | from django.conf import settings
3 |
4 | redis_db = redis.StrictRedis(host=settings.REDIS_HOST,
5 | port=settings.REDIS_PORT,
6 | db=settings.REDIS_DB,
7 | decode_responses=True)
8 |
--------------------------------------------------------------------------------
/food/__init__.py:
--------------------------------------------------------------------------------
1 | default_app_config = 'food.apps.FoodConfig'
--------------------------------------------------------------------------------
/food/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.core.urlresolvers import reverse
3 | from django.utils.html import format_html
4 |
5 | from comments.models import FoodComment
6 | from .models import FoodCategory, Food
7 |
8 | # Register your models here.
9 | class FoodCategoryAdmin(admin.ModelAdmin):
10 | model = FoodCategory
11 |
12 |
13 | class FoodCommentAdmin(admin.TabularInline):
14 | model = FoodComment
15 |
16 |
17 | class FoodAdmin(admin.ModelAdmin):
18 | model = Food
19 | list_display = ('title', 'category', 'created', 'show_firm_url')
20 |
21 | def show_firm_url(self, obj):
22 | return format_html("{url}", url=reverse('food:detail', kwargs={'food_id': obj.id}))
23 |
24 | show_firm_url.short_description = '地址'
25 |
26 | inlines = [FoodCommentAdmin]
27 |
28 | class Meta:
29 | ordering = ('-created',)
30 |
31 |
32 | admin.site.register(FoodCategory, FoodCategoryAdmin)
33 | admin.site.register(Food, FoodAdmin)
--------------------------------------------------------------------------------
/food/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class FoodConfig(AppConfig):
5 | name = 'food'
6 | verbose_name = '食品'
--------------------------------------------------------------------------------
/food/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 | from .models import Food
4 | from taggit.forms import TagWidget
5 |
6 |
7 | class FoodForm(forms.ModelForm):
8 |
9 | def __init__(self, *args, **kwargs):
10 | super(FoodForm, self).__init__(*args, **kwargs)
11 | self.fields['tags'].required = True
12 |
13 | def clean_tags(self):
14 | tags = self.cleaned_data['tags']
15 | if len(tags) > 5:
16 | raise forms.ValidationError('最多只能输入5个标签')
17 | return tags
18 |
19 | class Meta:
20 | model = Food
21 | fields = ('title', 'description', 'cover_image', 'link', 'category', 'tags')
22 | # widgets = {
23 | # 'tags': TagWidget(),
24 | # }
25 |
--------------------------------------------------------------------------------
/food/migrations/0002_auto_20160514_0738.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-14 07:38
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 | import taggit.managers
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('food', '0001_initial'),
13 | ]
14 |
15 | operations = [
16 | migrations.RemoveField(
17 | model_name='food',
18 | name='comments',
19 | ),
20 | migrations.AlterField(
21 | model_name='food',
22 | name='tags',
23 | field=taggit.managers.TaggableManager(blank=True, help_text='多个标签以逗号分隔', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='标签'),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/food/migrations/0003_auto_20160515_0738.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-15 07:38
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13 | ('food', '0002_auto_20160514_0738'),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterModelOptions(
18 | name='food',
19 | options={'verbose_name': '食品', 'verbose_name_plural': '食品'},
20 | ),
21 | migrations.AlterModelOptions(
22 | name='foodcategory',
23 | options={'verbose_name': '食品分类', 'verbose_name_plural': '食品分类'},
24 | ),
25 | migrations.RemoveField(
26 | model_name='food',
27 | name='users_collect',
28 | ),
29 | migrations.AddField(
30 | model_name='food',
31 | name='users_ate',
32 | field=models.ManyToManyField(blank=True, related_name='foods_ate', to=settings.AUTH_USER_MODEL, verbose_name='吃过的用户'),
33 | ),
34 | migrations.AddField(
35 | model_name='food',
36 | name='users_wta',
37 | field=models.ManyToManyField(blank=True, related_name='foods_wta', to=settings.AUTH_USER_MODEL, verbose_name='想吃的用户'),
38 | ),
39 | migrations.AlterField(
40 | model_name='food',
41 | name='cover_image',
42 | field=models.ImageField(upload_to='foods/cover/%Y/%m/%d', verbose_name='封面图片'),
43 | ),
44 | ]
45 |
--------------------------------------------------------------------------------
/food/migrations/0004_auto_20160522_1229.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-22 12:29
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('food', '0003_auto_20160515_0738'),
13 | ]
14 |
15 | operations = [
16 | migrations.AddField(
17 | model_name='food',
18 | name='total_dislikes',
19 | field=models.PositiveIntegerField(db_index=True, default=0, verbose_name='不喜欢数'),
20 | ),
21 | migrations.AddField(
22 | model_name='food',
23 | name='total_likes',
24 | field=models.PositiveIntegerField(db_index=True, default=0, verbose_name='喜欢数'),
25 | ),
26 | migrations.AlterField(
27 | model_name='food',
28 | name='category',
29 | field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='foods', to='food.FoodCategory', verbose_name='分类'),
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/food/migrations/0005_auto_20160522_1353.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-22 13:53
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('food', '0004_auto_20160522_1229'),
12 | ]
13 |
14 | operations = [
15 | migrations.RemoveField(
16 | model_name='food',
17 | name='total_dislikes',
18 | ),
19 | migrations.RemoveField(
20 | model_name='food',
21 | name='total_likes',
22 | ),
23 | migrations.RemoveField(
24 | model_name='food',
25 | name='users_dislike',
26 | ),
27 | migrations.RemoveField(
28 | model_name='food',
29 | name='users_like',
30 | ),
31 | migrations.AddField(
32 | model_name='food',
33 | name='rating_dislikes',
34 | field=models.PositiveIntegerField(blank=True, default=0, editable=False),
35 | ),
36 | migrations.AddField(
37 | model_name='food',
38 | name='rating_likes',
39 | field=models.PositiveIntegerField(blank=True, default=0, editable=False),
40 | ),
41 | ]
42 |
--------------------------------------------------------------------------------
/food/migrations/0006_auto_20160528_0511.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-28 05:11
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('food', '0005_auto_20160522_1353'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterModelOptions(
16 | name='food',
17 | options={'ordering': ('-created',), 'verbose_name': '食品', 'verbose_name_plural': '食品'},
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/food/migrations/0007_auto_20160604_0849.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-06-04 08:49
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('food', '0006_auto_20160528_0511'),
13 | ]
14 |
15 | operations = [
16 | migrations.AlterField(
17 | model_name='food',
18 | name='category',
19 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='foods', to='food.FoodCategory', verbose_name='分类'),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/food/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/food/migrations/__init__.py
--------------------------------------------------------------------------------
/food/models.py:
--------------------------------------------------------------------------------
1 | from django.core.urlresolvers import reverse
2 | from django.db import models
3 |
4 | from taggit.managers import TaggableManager
5 | from user.models import User
6 | from updown.fields import RatingField
7 |
8 |
9 | # Create your models here.
10 | class FoodCategory(models.Model):
11 | name = models.CharField(max_length=16, verbose_name='分类名称')
12 |
13 | def __str__(self):
14 | return self.name
15 |
16 | def get_absolute_url(self):
17 | return reverse('food:category', kwargs={'category': self.name})
18 |
19 | class Meta:
20 | verbose_name = '食品分类'
21 | verbose_name_plural = '食品分类'
22 |
23 |
24 | class Food(models.Model):
25 | title = models.CharField(max_length=128, verbose_name='标题')
26 | description = models.TextField(verbose_name='描述')
27 | cover_image = models.ImageField(upload_to='foods/cover/%Y/%m/%d', verbose_name='封面图片')
28 | rating = RatingField(can_change_vote=True)
29 | users_wta = models.ManyToManyField(User, related_name='foods_wta', blank=True, verbose_name='想吃的用户')
30 | users_ate = models.ManyToManyField(User, related_name='foods_ate', blank=True, verbose_name='吃过的用户')
31 | link = models.URLField(blank=True, verbose_name='相关链接')
32 | category = models.ForeignKey(FoodCategory, related_name='foods', verbose_name='分类')
33 | user = models.ForeignKey(User, related_name='foods_shared', verbose_name='创建用户')
34 | created = models.DateTimeField(auto_now_add=True, verbose_name='添加日期')
35 | tags = TaggableManager(blank=True, help_text='多个标签以逗号分隔', verbose_name="标签")
36 |
37 | @classmethod
38 | def get_food_categorys(self):
39 | return FoodCategory.objects.all()
40 |
41 | def get_absolute_url(self):
42 | return reverse('food:detail', kwargs={'food_id': self.id})
43 |
44 | def __str__(self):
45 | return self.title
46 |
47 | class Meta:
48 | verbose_name = '食品'
49 | verbose_name_plural = '食品'
50 | ordering = ('-created', )
51 |
52 |
--------------------------------------------------------------------------------
/food/templates/food/explore.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'home/base.tpl' %}
2 | {% load staticfiles %}
3 | {% block head %}
4 | 发现
5 |
6 | {% endblock head %}
7 | {% block content %}
8 |
9 |
15 |
16 |
17 | {% for food in foods %}
18 |
36 | {% endfor %}
37 |
38 |
39 | {% endblock content %}
40 | {% block js %}
41 |
42 | {% endblock js %}
--------------------------------------------------------------------------------
/food/templates/food/list.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'home/base.tpl' %}
2 | {% load staticfiles %}
3 | {% load thumbnail %}
4 | {% block head %}
5 | 好吃
6 |
7 | {% endblock head %}
8 | {% block content %}
9 |
10 |
11 |
12 | - 全部
13 | {% for category in categorys %}
14 | - {{ category }}
15 | {% endfor %}
16 |
17 |
18 |
19 |
20 | {% for food in foods %}
21 |
37 | {% endfor %}
38 |
39 |
40 | {% endblock content %}
41 | {% block js %}
42 |
43 | {% endblock js %}
44 |
--------------------------------------------------------------------------------
/food/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from .views import food_latest, food_detail, food_rate, food_category, food_tag, explore, hot, food_wta, food_ate, food_create
4 |
5 | urlpatterns = [
6 | url(r'^$', food_latest, name='list'),
7 | url(r'^create/$', food_create, name='create'),
8 | url(r'^(?P[\d]+)/$', food_detail, name='detail'),
9 | url(r'^rate/$', food_rate, name='rate'),
10 | url(r'^food_wta/$', food_wta, name='wta'),
11 | url(r'^food_ate/$', food_ate, name='ate'),
12 | url(r'^category/(?P[\w]+)/$', food_category, name='category'),
13 | url(r'^tag/(?P[\w]+)/$', food_tag, name='tag'),
14 | url(r'^explore/$', explore, name='explore'),
15 | url(r'^explore/hot/$', hot, name='hot'),
16 | ]
--------------------------------------------------------------------------------
/forum/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/forum/__init__.py
--------------------------------------------------------------------------------
/forum/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 | from comments.models import ForumPostComment
5 | from .models import ForumPost, Board
6 |
7 | # Register your models here.
8 | class BoardAdmin(admin.ModelAdmin):
9 | model = Board
10 |
11 | class ForumPostCommentAdmin(admin.TabularInline):
12 | model = ForumPostComment
13 |
14 |
15 | class ForumPostAdmin(admin.ModelAdmin):
16 | model = ForumPost
17 |
18 | inlines = [ForumPostCommentAdmin]
19 |
20 |
21 | admin.site.register(ForumPost, ForumPostAdmin)
22 | admin.site.register(Board, BoardAdmin)
23 |
--------------------------------------------------------------------------------
/forum/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ForumConfig(AppConfig):
5 | name = 'forum'
6 | verbose_name = '讨论区'
7 |
--------------------------------------------------------------------------------
/forum/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 | from .models import ForumPost
4 | from utils.mixins import CleanContentMixin
5 |
6 |
7 | class ForumPostForm(forms.ModelForm, CleanContentMixin):
8 | class Meta:
9 | model = ForumPost
10 | fields = ('title', 'content')
--------------------------------------------------------------------------------
/forum/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-10-03 09:30
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | initial = True
13 |
14 | dependencies = [
15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='Board',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('name', models.CharField(max_length=32, verbose_name='名称')),
24 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
25 | ('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='创建者')),
26 | ],
27 | ),
28 | migrations.CreateModel(
29 | name='ForumPost',
30 | fields=[
31 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
32 | ('title', models.CharField(max_length=64, verbose_name='帖子标题')),
33 | ('content', models.TextField(max_length=5096, verbose_name='帖子内容')),
34 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
35 | ('updated', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
36 | ('total_likes', models.PositiveIntegerField(default=0, verbose_name='推荐数')),
37 | ('board', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='forum.Board', verbose_name='所属板块')),
38 | ('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='创建者')),
39 | ('users_like', models.ManyToManyField(blank=True, related_name='forumposts_liked', to=settings.AUTH_USER_MODEL, verbose_name='推荐的用户')),
40 | ],
41 | ),
42 | ]
43 |
--------------------------------------------------------------------------------
/forum/migrations/0002_auto_20161008_1137.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-10-08 03:37
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('forum', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterModelOptions(
16 | name='board',
17 | options={'verbose_name': '板块', 'verbose_name_plural': '板块'},
18 | ),
19 | migrations.AlterModelOptions(
20 | name='forumpost',
21 | options={'verbose_name': '帖子', 'verbose_name_plural': '帖子'},
22 | ),
23 | migrations.AlterField(
24 | model_name='forumpost',
25 | name='total_likes',
26 | field=models.IntegerField(default=0, verbose_name='推荐数'),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/forum/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/forum/migrations/__init__.py
--------------------------------------------------------------------------------
/forum/models.py:
--------------------------------------------------------------------------------
1 | from django.core.urlresolvers import reverse
2 | from django.db import models
3 |
4 | from user.models import User
5 |
6 | # Create your models here.
7 | class Board(models.Model):
8 | creator = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='创建者')
9 | name = models.CharField(max_length=32, verbose_name='名称')
10 | created = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
11 |
12 | class Meta:
13 | verbose_name = '板块'
14 | verbose_name_plural = '板块'
15 |
16 | def __str__(self):
17 | return self.name
18 |
19 |
20 | class ForumPost(models.Model):
21 | title = models.CharField(max_length=64, verbose_name='帖子标题')
22 | content = models.TextField(max_length=5096, verbose_name='帖子内容')
23 | creator = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='创建者')
24 | board = models.ForeignKey(Board, blank=True, null=True, verbose_name='所属板块')
25 | created = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
26 | updated = models.DateTimeField(auto_now=True, verbose_name='更新时间')
27 | users_like = models.ManyToManyField(User, related_name='forumposts_liked', blank=True, verbose_name='推荐的用户')
28 | total_likes = models.IntegerField(default=0, verbose_name='推荐数')
29 |
30 | class Meta:
31 | verbose_name = '帖子'
32 | verbose_name_plural = '帖子'
33 |
34 | def __str__(self):
35 | return self.title
36 |
37 | def get_absolute_url(self):
38 | return reverse('forum:detail', kwargs={'post_id': self.id})
39 |
--------------------------------------------------------------------------------
/forum/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/forum/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from .views import post_index, delete_post, query_post, update_post, post_detail, post_like
4 |
5 | urlpatterns = [
6 | url(r'^$', post_index, name='index'),
7 | url(r'^post/(?P[\d]+)/$', post_detail, name='detail'),
8 | url(r'^post/(?P[\d]+)/like/$', post_like, name='like'),
9 | url(r'^delete/(?P[\d]+)/$', delete_post, name='delete'),
10 | url(r'^query/$', query_post, name='query'),
11 | url(r'^update/(?P[\d]+)/$', update_post, name='update'),
12 | ]
--------------------------------------------------------------------------------
/home/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/home/__init__.py
--------------------------------------------------------------------------------
/home/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/home/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class HomeConfig(AppConfig):
5 | name = 'home'
6 |
--------------------------------------------------------------------------------
/home/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/home/migrations/__init__.py
--------------------------------------------------------------------------------
/home/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/home/templates/home/base.tpl:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {% block head %}
10 | {% endblock head %}
11 |
12 |
13 | {% include 'home/navbar.tpl' %}
14 | {% block content %}
15 | {% endblock content %}
16 | {% include 'user/chat.tpl' %}
17 |
18 |
19 |
20 |
21 |
22 | {% block js %}
23 | {% endblock js %}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/home/templates/home/index.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'home/base.tpl' %}
2 | {% load staticfiles %}
3 | {% load thumbnail %}
4 | {% block head %}
5 | 首页
6 |
7 | {% endblock head %}
8 | {% block content %}
9 |
10 |
11 |
12 |
13 | 一起分享和发现好吃的
14 |
15 |
16 |
17 | Pineapple
18 |
19 |
20 |
21 |
22 |
23 |
24 |
精选专题
25 |
26 |
27 | {% for topic in topics %}
28 |
41 | {% endfor %}
42 |
43 |
44 | {% endblock content %}
45 | {% block js %}
46 |
47 | {% endblock js %}
48 |
--------------------------------------------------------------------------------
/home/templates/home/navbar.tpl:
--------------------------------------------------------------------------------
1 | {% load widget_tweaks %}
2 |
3 |
4 |
5 | -
6 | 首页
7 |
8 |
9 | -
10 | 专题
11 |
12 |
13 | -
14 | 发现
15 |
16 |
17 | -
18 | 好吃
19 |
20 |
21 | -
22 | 讨论区
23 |
24 |
25 |
26 | {% if request.user.is_authenticated %}
27 |
28 |
29 |
32 |
38 |
39 | {% else %}
40 |
41 |
42 | {% endif %}
43 |
47 |
48 |
--------------------------------------------------------------------------------
/home/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/home/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from .views import home
4 |
5 | urlpatterns = [
6 | url(r'', home, name='index'),
7 | ]
--------------------------------------------------------------------------------
/home/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.views.decorators.cache import cache_page
3 |
4 | from actions.models import Action
5 | from search.forms import SearchForm
6 | from topic.models import FoodTopic
7 |
8 | # 首页
9 | # @cache_page(60)
10 | def home(request):
11 | form = SearchForm()
12 | topics = FoodTopic.objects.order_by('-total_collects').all()[:4]
13 | return render(request, 'home/index.tpl', {
14 | 'search_form': form,
15 | 'topics': topics
16 | })
17 |
--------------------------------------------------------------------------------
/location/__init__.py:
--------------------------------------------------------------------------------
1 | default_app_config = 'location.apps.LocationConfig'
--------------------------------------------------------------------------------
/location/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import Province, City
3 |
4 |
5 | # Register your models here.
6 | class CityAdmin(admin.TabularInline):
7 | model = City
8 |
9 |
10 | class ProvinceAdmin(admin.ModelAdmin):
11 | model = Province
12 |
13 | inlines = [
14 | CityAdmin,
15 | ]
16 |
17 |
18 | admin.site.register(Province, ProvinceAdmin)
--------------------------------------------------------------------------------
/location/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class LocationConfig(AppConfig):
5 | name = 'location'
6 | verbose_name = '位置'
--------------------------------------------------------------------------------
/location/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-08 12:55
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | ]
15 |
16 | operations = [
17 | migrations.CreateModel(
18 | name='City',
19 | fields=[
20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21 | ('name', models.CharField(max_length=32, verbose_name='城市名称')),
22 | ],
23 | options={
24 | 'verbose_name_plural': '城市',
25 | 'verbose_name': '城市',
26 | },
27 | ),
28 | migrations.CreateModel(
29 | name='Province',
30 | fields=[
31 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
32 | ('name', models.CharField(max_length=32, verbose_name='省份名称')),
33 | ],
34 | options={
35 | 'verbose_name_plural': '省份',
36 | 'verbose_name': '省份',
37 | },
38 | ),
39 | migrations.AddField(
40 | model_name='city',
41 | name='province',
42 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='location.Province', verbose_name='省份'),
43 | ),
44 | ]
45 |
--------------------------------------------------------------------------------
/location/migrations/0002_place.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-13 05:09
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('location', '0001_initial'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='Place',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('description', models.CharField(max_length=128, verbose_name='地点名称')),
21 | ('city', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='location.City', verbose_name='所在城市')),
22 | ],
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/location/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/location/migrations/__init__.py
--------------------------------------------------------------------------------
/location/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 | class Province(models.Model):
5 | name = models.CharField(max_length=32, verbose_name='省份名称')
6 |
7 | def __str__(self):
8 | return self.name
9 |
10 | class Meta:
11 | verbose_name = '省份'
12 | verbose_name_plural = '省份'
13 |
14 |
15 | class City(models.Model):
16 | name = models.CharField(max_length=32, verbose_name='城市名称')
17 | province = models.ForeignKey(Province, verbose_name='省份')
18 |
19 | def __str__(self):
20 | return self.name
21 |
22 | class Meta:
23 | verbose_name = '城市'
24 | verbose_name_plural = '城市'
25 |
26 |
27 | class Place(models.Model):
28 | description = models.CharField(max_length=128, verbose_name='地点名称')
29 | city = models.ForeignKey(City, verbose_name='所在城市')
30 |
--------------------------------------------------------------------------------
/location/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/location/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pineapple.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/media/cache/0e/35/0e356957b375c31f151c91eadff99124.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/0e/35/0e356957b375c31f151c91eadff99124.jpg
--------------------------------------------------------------------------------
/media/cache/0e/9f/0e9fd5ae5087c241754bb70837e94186.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/0e/9f/0e9fd5ae5087c241754bb70837e94186.jpg
--------------------------------------------------------------------------------
/media/cache/20/30/203029d21c730d8b90087d5f7b586420.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/20/30/203029d21c730d8b90087d5f7b586420.jpg
--------------------------------------------------------------------------------
/media/cache/26/e5/26e58558b4f30fd3608eea3b625255a2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/26/e5/26e58558b4f30fd3608eea3b625255a2.jpg
--------------------------------------------------------------------------------
/media/cache/2a/95/2a95bc9e92a1bb2abb8465efb383a00c.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/2a/95/2a95bc9e92a1bb2abb8465efb383a00c.jpg
--------------------------------------------------------------------------------
/media/cache/38/57/3857eb185e17c4c4c80b10d1c573cd6f.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/38/57/3857eb185e17c4c4c80b10d1c573cd6f.jpg
--------------------------------------------------------------------------------
/media/cache/3d/5e/3d5e42e6f394d5ce7c03bde326490b34.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/3d/5e/3d5e42e6f394d5ce7c03bde326490b34.jpg
--------------------------------------------------------------------------------
/media/cache/4d/bf/4dbf961fd10ba4b0309ecbae8daa135e.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/4d/bf/4dbf961fd10ba4b0309ecbae8daa135e.jpg
--------------------------------------------------------------------------------
/media/cache/57/ea/57ea8394398cb375b9eb39181c1c2fb9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/57/ea/57ea8394398cb375b9eb39181c1c2fb9.jpg
--------------------------------------------------------------------------------
/media/cache/58/d9/58d95a5917d42a0a7e85fb6b007e93ee.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/58/d9/58d95a5917d42a0a7e85fb6b007e93ee.jpg
--------------------------------------------------------------------------------
/media/cache/69/c8/69c834a00be1a190b360d2e1a5104c81.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/69/c8/69c834a00be1a190b360d2e1a5104c81.jpg
--------------------------------------------------------------------------------
/media/cache/6a/47/6a474beda343a724939a72b5dca05aa0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/6a/47/6a474beda343a724939a72b5dca05aa0.jpg
--------------------------------------------------------------------------------
/media/cache/6d/50/6d50de93c73b2f41f09b409c75e44277.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/6d/50/6d50de93c73b2f41f09b409c75e44277.jpg
--------------------------------------------------------------------------------
/media/cache/6f/4d/6f4d7d3d9c28a4dfa5d58d908c3a4d38.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/6f/4d/6f4d7d3d9c28a4dfa5d58d908c3a4d38.jpg
--------------------------------------------------------------------------------
/media/cache/70/08/700890c160c23fbe55b81af05cdff76d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/70/08/700890c160c23fbe55b81af05cdff76d.jpg
--------------------------------------------------------------------------------
/media/cache/74/b0/74b098dd53b0b05ca6faac60cb3dcbb6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/74/b0/74b098dd53b0b05ca6faac60cb3dcbb6.jpg
--------------------------------------------------------------------------------
/media/cache/7d/37/7d372859d9202485c95f6782d118148a.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/7d/37/7d372859d9202485c95f6782d118148a.jpg
--------------------------------------------------------------------------------
/media/cache/aa/59/aa59a51567a07379fe4fef58513bce9d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/aa/59/aa59a51567a07379fe4fef58513bce9d.jpg
--------------------------------------------------------------------------------
/media/cache/b0/17/b017c6a13ffcf20d25e119f44543da24.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/b0/17/b017c6a13ffcf20d25e119f44543da24.jpg
--------------------------------------------------------------------------------
/media/cache/ba/9a/ba9a8347cf2cf76131439915168c7302.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/ba/9a/ba9a8347cf2cf76131439915168c7302.jpg
--------------------------------------------------------------------------------
/media/cache/c5/f8/c5f8d4297de18fc4a2b70fe2ab92d39e.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/c5/f8/c5f8d4297de18fc4a2b70fe2ab92d39e.jpg
--------------------------------------------------------------------------------
/media/cache/d6/9b/d69b7579b64edd8d840b90a1b03706fe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/d6/9b/d69b7579b64edd8d840b90a1b03706fe.jpg
--------------------------------------------------------------------------------
/media/cache/da/6a/da6a6363612d0a90fd6aa071281f9b60.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/da/6a/da6a6363612d0a90fd6aa071281f9b60.jpg
--------------------------------------------------------------------------------
/media/cache/de/eb/deebf2d808d9329fcdde3e21a4f4507a.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/de/eb/deebf2d808d9329fcdde3e21a4f4507a.jpg
--------------------------------------------------------------------------------
/media/cache/e0/52/e0528d9879ecb13a6757be825f26f306.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/e0/52/e0528d9879ecb13a6757be825f26f306.jpg
--------------------------------------------------------------------------------
/media/cache/e2/f8/e2f80c38dd496f65d41964304e32de63.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/e2/f8/e2f80c38dd496f65d41964304e32de63.jpg
--------------------------------------------------------------------------------
/media/cache/f9/f1/f9f1a8adb2a51f18dd1efcc49f17ecb8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/f9/f1/f9f1a8adb2a51f18dd1efcc49f17ecb8.jpg
--------------------------------------------------------------------------------
/media/cache/fa/cb/facb22872d24e58db5ca6c6b69269cdd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/cache/fa/cb/facb22872d24e58db5ca6c6b69269cdd.jpg
--------------------------------------------------------------------------------
/media/foods/cover/2016/05/22/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/foods/cover/2016/05/22/avatar.png
--------------------------------------------------------------------------------
/media/foods/cover/2016/05/25/food3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/foods/cover/2016/05/25/food3.jpg
--------------------------------------------------------------------------------
/media/foods/cover/2016/05/28/b219ebc4b74543a941fbf6af1c178a82b80114f4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/foods/cover/2016/05/28/b219ebc4b74543a941fbf6af1c178a82b80114f4.jpg
--------------------------------------------------------------------------------
/media/foods/cover/2016/06/05/543198769.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/foods/cover/2016/06/05/543198769.jpg
--------------------------------------------------------------------------------
/media/foods/cover/2016/06/05/543198769_YloxsaV.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/foods/cover/2016/06/05/543198769_YloxsaV.jpg
--------------------------------------------------------------------------------
/media/foods/cover/2016/06/05/543198769_r9eurv5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/foods/cover/2016/06/05/543198769_r9eurv5.jpg
--------------------------------------------------------------------------------
/media/foods/cover/2016/06/05/543198769_z9W188q.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/foods/cover/2016/06/05/543198769_z9W188q.jpg
--------------------------------------------------------------------------------
/media/foods/cover/2016/06/05/boluobao.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/foods/cover/2016/06/05/boluobao.jpg
--------------------------------------------------------------------------------
/media/topic/cover/2016/05/25/topic5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/topic/cover/2016/05/25/topic5.jpg
--------------------------------------------------------------------------------
/media/users/avatar/2016/06/04/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/users/avatar/2016/06/04/avatar.png
--------------------------------------------------------------------------------
/media/users/avatar/2016/06/07/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/users/avatar/2016/06/07/avatar.png
--------------------------------------------------------------------------------
/media/users/avatar/2016/10/06/7101967.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/media/users/avatar/2016/10/06/7101967.jpeg
--------------------------------------------------------------------------------
/message/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/message/__init__.py
--------------------------------------------------------------------------------
/message/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/message/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class MessageConfig(AppConfig):
5 | name = 'message'
6 |
--------------------------------------------------------------------------------
/message/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/message/migrations/__init__.py
--------------------------------------------------------------------------------
/message/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/message/tasks.py:
--------------------------------------------------------------------------------
1 | from celery import task
2 | from celery.decorators import periodic_task
3 | from constants import *
4 |
5 | from ext import redis_db as rds
6 | from time import time
7 |
8 | import datetime
9 | import json
10 |
11 | @task
12 | def send_msg(sender, receiver, text):
13 | t = time()
14 | msg = json.dumps({
15 | 'from': sender,
16 | 'to': receiver,
17 | 'msg': text,
18 | 't': t
19 | })
20 | receiver_key = REDIS_MESSAGES_KEY.format(receiver)
21 | sender_key = REDIS_MESSAGES_KEY.format(sender)
22 | rds.incr(REDIS_MESSAGES_UNREAD_KEY.format(receiver), 1)
23 | rds.lpush(receiver_key, msg)
24 | rds.ltrim(receiver_key, 0, MAX_MESSAGES_COUNT)
25 | rds.lpush(sender_key, msg)
26 | rds.ltrim(sender_key, 0, MAX_MESSAGES_COUNT)
27 | # 添加到最近活动集合
28 | rds.zadd(REDIS_MESSAGE_USERS_KEY, t, receiver)
29 | rds.zadd(REDIS_MESSAGE_USERS_KEY, t, sender)
30 | return True
31 |
32 |
33 | # 清理不活跃的key
34 | @periodic_task(run_every=datetime.timedelta(days=7))
35 | def clean_inactive_msg():
36 | now_time = time()
37 | user_ids = rds.zrangebyscore(REDIS_MESSAGE_USERS_KEY, 0, now_time - MESSAGES_TIMEOUT)
38 | for uid in user_ids:
39 | rds.delete(REDIS_MESSAGES_KEY.format(uid))
40 | rds.delete(REDIS_MESSAGES_UNREAD_KEY.format(uid))
41 | rds.zremrangebyscore(REDIS_MESSAGE_USERS_KEY, 0, now_time - MESSAGES_TIMEOUT)
42 |
--------------------------------------------------------------------------------
/message/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/message/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from .views import send, pull, attention, user_base_profile, pull_new_msgs
4 |
5 | urlpatterns = [
6 | url(r'^send/$', send, name='send'),
7 | url(r'^pull/$', pull, name='pull'),
8 | url(r'^attention/$', attention, name='attention'),
9 | url(r'^new/$', pull_new_msgs, name='pull_new'),
10 | url(r'^profile/$', user_base_profile, name='profile'),
11 | ]
--------------------------------------------------------------------------------
/message/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.contrib.auth.decorators import login_required
3 | from django.db.models import F
4 | from django.http import JsonResponse, HttpResponse
5 |
6 | from constants import *
7 | from ext import redis_db as rds
8 | from user.models import User, UserProfile
9 | from .tasks import send_msg
10 |
11 | import json
12 |
13 | # Create your views here.
14 | @login_required
15 | def send(request):
16 | text = request.POST.get('msg')
17 | receiver = int(request.POST.get('user'))
18 | if len(text) > MAX_MESSAGE_LENGTH:
19 | # 限制消息长度
20 | return JsonResponse(JSON_FAIL(MAX_MESSAGE_LENGTH_REACH))
21 | sender = request.user.id
22 | send_msg(sender, receiver, text)
23 | # send_msg.delay(sender, receiver, text) # 交给celery完成
24 | return JsonResponse(JSON_SUCCESS)
25 |
26 | @login_required
27 | def pull(request):
28 | receiver = request.user.id
29 | rds.set(REDIS_MESSAGES_UNREAD_KEY.format(receiver), 0)
30 | messages = rds.lrange(REDIS_MESSAGES_KEY.format(receiver), 0, MAX_MESSAGES_COUNT)
31 | messages = [json.loads(msg) for msg in messages]
32 | # return HttpResponse(str(messages), content_type='application/json')
33 | return JsonResponse(JSON_SUCCESS_WITH_DATA({'data': messages}), safe=False)
34 |
35 | @login_required
36 | def attention(request):
37 | receiver = request.user.id
38 | msg_count = rds.get(REDIS_MESSAGES_UNREAD_KEY.format(receiver))
39 | if not msg_count:
40 | msg_count = 0
41 | return JsonResponse(JSON_SUCCESS_WITH_DATA({'count': int(msg_count)}))
42 |
43 | @login_required
44 | def pull_new_msgs(request):
45 | last_time = float(request.GET.get('time', -1))
46 | if last_time < 0:
47 | return JsonResponse(JSON_FAIL(INVALID_TIMESTAMP))
48 | receiver = request.user.id
49 | messages = rds.lrange(REDIS_MESSAGES_KEY.format(receiver), 0, MAX_MESSAGES_COUNT)
50 | new_msgs = []
51 | for msg in messages:
52 | msg = json.loads(msg)
53 | if float(msg['t']) <= float(last_time):
54 | break
55 | new_msgs.append(msg)
56 | return JsonResponse(JSON_SUCCESS_WITH_DATA({'data': new_msgs}), safe=False)
57 |
58 |
59 | @login_required
60 | def user_base_profile(request):
61 | ids = json.loads(request.POST.get('ids', '[]'))
62 | if len(ids) > MAX_CONTACT_COUNT:
63 | return JsonResponse(JSON_FAIL(MAX_CONTACT_COUNT_REACH))
64 | if not ids:
65 | profiles = []
66 | else:
67 | profiles = list(UserProfile.objects.filter(user__in=ids).annotate(name=F('user__username')).values('user', 'name', 'avatar'))
68 | return JsonResponse(JSON_SUCCESS_WITH_DATA({'data': profiles}), safe=False)
69 |
70 |
71 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pineapple",
3 | "version": "1.0.0",
4 | "description": "get more",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/pineapplers/pineapple.git"
12 | },
13 | "keywords": [
14 | "bingolee"
15 | ],
16 | "author": "Rlilyyy",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/pineapplers/pineapple/issues"
20 | },
21 | "homepage": "https://github.com/pineapplers/pineapple#readme",
22 | "devDependencies": {
23 | "babel-cli": "^6.9.0",
24 | "babel-core": "^6.8.0",
25 | "babel-loader": "^6.2.4",
26 | "babel-polyfill": "^6.9.1",
27 | "babel-preset-es2015": "^6.9.0",
28 | "css-loader": "^0.23.1",
29 | "extract-text-webpack-plugin": "^1.0.1",
30 | "glob": "^7.0.3",
31 | "html-loader": "^0.4.3",
32 | "html-webpack-plugin": "^2.16.1",
33 | "less": "^2.7.0",
34 | "less-loader": "^2.2.3",
35 | "path": "^0.12.7",
36 | "style-loader": "^0.13.1",
37 | "webpack": "^1.13.0",
38 | "webpack-dev-server": "^1.14.1"
39 | }
40 | }
--------------------------------------------------------------------------------
/pineapple/__init__.py:
--------------------------------------------------------------------------------
1 | from .celery import app as celery_app
2 |
--------------------------------------------------------------------------------
/pineapple/celery.py:
--------------------------------------------------------------------------------
1 | import os
2 | from celery import Celery
3 | from django.conf import settings
4 |
5 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pineapple.settings')
6 |
7 | app = Celery('pineapple')
8 |
9 | app.config_from_object('django.conf:settings')
10 | app.autodiscover_tasks([])
11 |
--------------------------------------------------------------------------------
/pineapple/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/pineapple/management/__init__.py
--------------------------------------------------------------------------------
/pineapple/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/pineapple/management/commands/__init__.py
--------------------------------------------------------------------------------
/pineapple/management/commands/load_fixtures.py:
--------------------------------------------------------------------------------
1 | from django.core.management.base import BaseCommand
2 |
3 |
4 | class Command(BaseCommand):
5 | def handle(self, *args, **options):
6 | from django.core.management import execute_from_command_line
7 | import sys
8 |
9 | execute_from_command_line([sys.argv[0], 'loaddata', 'initial_data'])
10 |
--------------------------------------------------------------------------------
/pineapple/urls.py:
--------------------------------------------------------------------------------
1 | """pineapple URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.9/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: url(r'^$', 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: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.conf.urls import url, include
14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
15 | """
16 | from django.conf import settings
17 | from django.conf.urls import url, include, handler404, handler500
18 | from django.conf.urls.static import static
19 | from django.contrib import admin
20 |
21 | from home.views import home
22 |
23 | admin.site.site_header = 'Pineapple'
24 | admin.site.site_title = 'Pineapple Admin'
25 |
26 | handler404 = 'pineapple.views.page_not_found'
27 | handler500 = 'pineapple.views.server_err'
28 |
29 | urlpatterns = [
30 | url(r'^$', home),
31 | url(r'^admin/', admin.site.urls),
32 | url(r'^home/', include('home.urls', namespace='home')),
33 | url(r'^user/', include('user.urls', namespace='user')),
34 | url(r'^search/', include('search.urls', namespace='search')),
35 | url(r'^topic/', include('topic.urls', namespace='topic')),
36 | url(r'^food/', include('food.urls', namespace='food')),
37 | url(r'^message/', include('message.urls', namespace='message')),
38 | url(r'^forum/', include('forum.urls', namespace='forum')),
39 | ] + static('public/static', document_root=settings.STATIC_ROOT) + static('media/', document_root=settings.MEDIA_ROOT)
40 |
41 |
--------------------------------------------------------------------------------
/pineapple/views.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpResponse
2 |
3 | # 404
4 | def page_not_found(request):
5 | return HttpResponse('page not found!')
6 |
7 | # 500
8 | def server_err(request):
9 | return HttpResponse('server error')
10 |
--------------------------------------------------------------------------------
/pineapple/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for pineapple 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/1.9/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", "pineapple.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/public/static/assets/particles.json:
--------------------------------------------------------------------------------
1 | {
2 | "particles": {
3 | "number": {
4 | "value": 80,
5 | "density": {
6 | "enable": true,
7 | "value_area": 800
8 | }
9 | },
10 | "color": {
11 | "value": "#ECEDEE"
12 | },
13 | "shape": {
14 | "type": "circle",
15 | "stroke": {
16 | "width": 0.5,
17 | "color": "#000000"
18 | },
19 | "polygon": {
20 | "nb_sides": 5
21 | },
22 | "image": {
23 | "src": "img/github.svg",
24 | "width": 100,
25 | "height": 100
26 | }
27 | },
28 | "opacity": {
29 | "value": 0.8,
30 | "random": false,
31 | "anim": {
32 | "enable": false,
33 | "speed": 1,
34 | "opacity_min": 0.1,
35 | "sync": false
36 | }
37 | },
38 | "size": {
39 | "value": 8,
40 | "random": true,
41 | "anim": {
42 | "enable": false,
43 | "speed": 1,
44 | "size_min": 0.1,
45 | "sync": false
46 | }
47 | },
48 | "line_linked": {
49 | "enable": false,
50 | "distance": 300,
51 | "color": "#ffffff",
52 | "opacity": 0.4,
53 | "width": 2
54 | },
55 | "move": {
56 | "enable": true,
57 | "speed": 12,
58 | "direction": "none",
59 | "random": false,
60 | "straight": false,
61 | "out_mode": "out",
62 | "bounce": false,
63 | "attract": {
64 | "enable": false,
65 | "rotateX": 600,
66 | "rotateY": 1200
67 | }
68 | }
69 | },
70 | "interactivity": {
71 | "detect_on": "canvas",
72 | "events": {
73 | "onhover": {
74 | "enable": false,
75 | "mode": "repulse"
76 | },
77 | "onclick": {
78 | "enable": true,
79 | "mode": "push"
80 | },
81 | "resize": true
82 | },
83 | "modes": {
84 | "grab": {
85 | "distance": 800,
86 | "line_linked": {
87 | "opacity": 1
88 | }
89 | },
90 | "bubble": {
91 | "distance": 800,
92 | "size": 80,
93 | "duration": 2,
94 | "opacity": 0.8,
95 | "speed": 1
96 | },
97 | "repulse": {
98 | "distance": 400,
99 | "duration": 0.4
100 | },
101 | "push": {
102 | "particles_nb": 4
103 | },
104 | "remove": {
105 | "particles_nb": 2
106 | }
107 | }
108 | },
109 | "retina_detect": true
110 | }
--------------------------------------------------------------------------------
/public/static/css/fonts/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/css/fonts/icomoon.eot
--------------------------------------------------------------------------------
/public/static/css/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/css/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/public/static/css/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/css/fonts/icomoon.woff
--------------------------------------------------------------------------------
/public/static/css/food_list.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin-top: 80px;
3 | min-width: 1100px;
4 | background-color: #f1f1f1;
5 | }
6 | .navbar {
7 | background-image: url("/public/static/images/home-header-bg-563ac1688e.png");
8 | transition: background-color ease-in-out 0.2s;
9 | -o-transition: background-color ease-in-out 0.2s;
10 | -ms-transition: background-color ease-in-out 0.2s;
11 | -moz-transition: background-color ease-in-out 0.2s;
12 | -webkit-transition: background-color ease-in-out 0.2s;
13 | box-shadow: 0 0 4px rgba(0, 0, 0, 0);
14 | }
15 | .list-container {
16 | width: 1020px;
17 | margin: 0 auto;
18 | }
19 | .list-container .list-tab-container {
20 | width: 100%;
21 | padding: 0 15px;
22 | padding-bottom: 35px;
23 | }
24 | .list-container .list-tab-container .tab-ul {
25 | list-style-type: none;
26 | margin: 0;
27 | padding: 0;
28 | }
29 | .list-container .list-tab-container .tab-ul .tab-ul-item {
30 | float: left;
31 | display: block;
32 | width: 90px;
33 | height: 70px;
34 | line-height: 70px;
35 | font-size: 1.6rem;
36 | text-align: center;
37 | color: #909090;
38 | }
39 | .list-container .list-tab-container .tab-ul .current-item {
40 | color: #5d5d5d;
41 | border-bottom: 2px solid #40a3c2;
42 | }
43 | .list-container .list-main-container .item {
44 | float: left;
45 | width: 300px;
46 | margin: 20px;
47 | border-top-left-radius: 5px;
48 | border-top-right-radius: 5px;
49 | background-color: white;
50 | transition: box-shadow ease 0.3s;
51 | -o-transition: box-shadow ease 0.3s;
52 | -ms-transition: box-shadow ease 0.3s;
53 | -moz-transition: box-shadow ease 0.3s;
54 | -webkit-transition: box-shadow ease 0.3s;
55 | overflow: hidden;
56 | }
57 | .list-container .list-main-container .item:hover {
58 | box-shadow: 0 0 8px #939393;
59 | }
60 | .list-container .list-main-container .item a {
61 | text-decoration: none;
62 | color: inherit;
63 | }
64 | .list-container .list-main-container .item .item-img {
65 | height: 200px;
66 | background-position: center;
67 | background-repeat: no-repeat;
68 | background-size: cover;
69 | }
70 | .list-container .list-main-container .item .item-main {
71 | padding-bottom: 20px;
72 | }
73 | .list-container .list-main-container .item .item-main .item-title {
74 | margin: 0;
75 | padding: 12px 0;
76 | text-align: center;
77 | font-weight: normal;
78 | color: #40a3c2;
79 | }
80 | .list-container .list-main-container .item .item-tags {
81 | height: 22px;
82 | overflow: hidden;
83 | padding: 0px 15px;
84 | color: #40a3c2;
85 | font-size: 1.6rem;
86 | }
87 | .list-container .list-main-container .item .item-tags .tag {
88 | display: inline-block;
89 | font-size: 1.4rem;
90 | margin: 0 8px 5px 8px;
91 | }
92 |
--------------------------------------------------------------------------------
/public/static/css/forum_list.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin-top: 80px;
3 | min-width: 1100px;
4 | background-color: #f1f1f1;
5 | }
6 | .navbar {
7 | background-image: url("/public/static/images/home-header-bg-563ac1688e.png");
8 | transition: background-color ease-in-out 0.2s;
9 | -o-transition: background-color ease-in-out 0.2s;
10 | -ms-transition: background-color ease-in-out 0.2s;
11 | -moz-transition: background-color ease-in-out 0.2s;
12 | -webkit-transition: background-color ease-in-out 0.2s;
13 | box-shadow: 0 0 4px rgba(0, 0, 0, 0);
14 | }
15 | .list-container {
16 | width: 1020px;
17 | margin: 0 auto;
18 | }
19 | .post-bar {
20 | background-color: #fff;
21 | min-height: 300px;
22 | max-height: 1000px;
23 | margin-bottom: 20px;
24 | }
25 | .post-title {
26 | outline: none;
27 | width: 100%;
28 | height: 30px;
29 | border-radius: 5px;
30 | border: 1px solid #ccc;
31 | }
32 | .post-content {
33 | width: 100%;
34 | height: 200px;
35 | border-radius: 5px;
36 | border: 1px solid #ccc;
37 | }
38 | .post-submit {
39 | margin: 10px;
40 | float: right;
41 | width: 100px;
42 | height: 30px;
43 | background-color: #40a3c2;
44 | color: #fff;
45 | border: none;
46 | font-size: 16px;
47 | }
48 | table {
49 | width: 100%;
50 | }
51 | .post-board {
52 | margin-left: 30px;
53 | height: 50px;
54 | width: 100%;
55 | line-height: 50px;
56 | font-size: 20px;
57 | color: #778087;
58 | }
59 | .post-search {
60 | border-radius: 5px;
61 | border: 1px solid #ccc;
62 | width: 200px;
63 | height: 30px;
64 | margin-left: 20px;
65 | }
66 | .post-search-btn {
67 | height: 30px;
68 | width: 50px;
69 | background-color: #40a3c2;
70 | color: #fff;
71 | border: none;
72 | margin: auto 10px;
73 | }
74 | .post-order {
75 | text-align: right;
76 | padding: 0 10px 0px;
77 | }
78 | .post-order a {
79 | text-decoration: none;
80 | color: #40a3c2;
81 | }
82 | .post-order .active {
83 | color: #000;
84 | cursor: default;
85 | }
86 | .post {
87 | padding: 10px;
88 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.1);
89 | }
90 | .post a {
91 | color: #778087;
92 | text-decoration: none;
93 | cursor: pointer;
94 | }
95 | .rating {
96 | width: 5%;
97 | }
98 | .board {
99 | width: 10%;
100 | }
101 | .title {
102 | font-size: 16px;
103 | width: 75%;
104 | text-overflow : ellipsis;
105 | white-space : nowrap;
106 | overflow : hidden;
107 | }
108 | .creator {
109 | width: 10%;
110 | }
111 | .switch-page {
112 | padding: 5px 5px;
113 | float: right;
114 | text-decoration: none;
115 | color: #40a3c2;
116 | }
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/HELP-US-OUT.txt:
--------------------------------------------------------------------------------
1 | I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project,
2 | Fonticons (https://fonticons.com). It makes it easy to put the perfect icons on your website. Choose from our awesome,
3 | comprehensive icon sets or copy and paste your own.
4 |
5 | Please. Check it out.
6 |
7 | -Dave Gandy
8 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/font/font-awesome-4.5.0/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/font/font-awesome-4.5.0/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/font/font-awesome-4.5.0/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/font/font-awesome-4.5.0/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/font/font-awesome-4.5.0/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/animated.less:
--------------------------------------------------------------------------------
1 | // Animated Icons
2 | // --------------------------
3 |
4 | .@{fa-css-prefix}-spin {
5 | -webkit-animation: fa-spin 2s infinite linear;
6 | animation: fa-spin 2s infinite linear;
7 | }
8 |
9 | .@{fa-css-prefix}-pulse {
10 | -webkit-animation: fa-spin 1s infinite steps(8);
11 | animation: fa-spin 1s infinite steps(8);
12 | }
13 |
14 | @-webkit-keyframes fa-spin {
15 | 0% {
16 | -webkit-transform: rotate(0deg);
17 | transform: rotate(0deg);
18 | }
19 | 100% {
20 | -webkit-transform: rotate(359deg);
21 | transform: rotate(359deg);
22 | }
23 | }
24 |
25 | @keyframes fa-spin {
26 | 0% {
27 | -webkit-transform: rotate(0deg);
28 | transform: rotate(0deg);
29 | }
30 | 100% {
31 | -webkit-transform: rotate(359deg);
32 | transform: rotate(359deg);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/bordered-pulled.less:
--------------------------------------------------------------------------------
1 | // Bordered & Pulled
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-border {
5 | padding: .2em .25em .15em;
6 | border: solid .08em @fa-border-color;
7 | border-radius: .1em;
8 | }
9 |
10 | .@{fa-css-prefix}-pull-left { float: left; }
11 | .@{fa-css-prefix}-pull-right { float: right; }
12 |
13 | .@{fa-css-prefix} {
14 | &.@{fa-css-prefix}-pull-left { margin-right: .3em; }
15 | &.@{fa-css-prefix}-pull-right { margin-left: .3em; }
16 | }
17 |
18 | /* Deprecated as of 4.4.0 */
19 | .pull-right { float: right; }
20 | .pull-left { float: left; }
21 |
22 | .@{fa-css-prefix} {
23 | &.pull-left { margin-right: .3em; }
24 | &.pull-right { margin-left: .3em; }
25 | }
26 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/core.less:
--------------------------------------------------------------------------------
1 | // Base Class Definition
2 | // -------------------------
3 |
4 | .@{fa-css-prefix} {
5 | display: inline-block;
6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/fixed-width.less:
--------------------------------------------------------------------------------
1 | // Fixed Width Icons
2 | // -------------------------
3 | .@{fa-css-prefix}-fw {
4 | width: (18em / 14);
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/font-awesome.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */
5 |
6 | @import "variables.less";
7 | @import "mixins.less";
8 | @import "path.less";
9 | @import "core.less";
10 | @import "larger.less";
11 | @import "fixed-width.less";
12 | @import "list.less";
13 | @import "bordered-pulled.less";
14 | @import "animated.less";
15 | @import "rotated-flipped.less";
16 | @import "stacked.less";
17 | @import "icons.less";
18 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/larger.less:
--------------------------------------------------------------------------------
1 | // Icon Sizes
2 | // -------------------------
3 |
4 | /* makes the font 33% larger relative to the icon container */
5 | .@{fa-css-prefix}-lg {
6 | font-size: (4em / 3);
7 | line-height: (3em / 4);
8 | vertical-align: -15%;
9 | }
10 | .@{fa-css-prefix}-2x { font-size: 2em; }
11 | .@{fa-css-prefix}-3x { font-size: 3em; }
12 | .@{fa-css-prefix}-4x { font-size: 4em; }
13 | .@{fa-css-prefix}-5x { font-size: 5em; }
14 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/list.less:
--------------------------------------------------------------------------------
1 | // List Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-ul {
5 | padding-left: 0;
6 | margin-left: @fa-li-width;
7 | list-style-type: none;
8 | > li { position: relative; }
9 | }
10 | .@{fa-css-prefix}-li {
11 | position: absolute;
12 | left: -@fa-li-width;
13 | width: @fa-li-width;
14 | top: (2em / 14);
15 | text-align: center;
16 | &.@{fa-css-prefix}-lg {
17 | left: (-@fa-li-width + (4em / 14));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/mixins.less:
--------------------------------------------------------------------------------
1 | // Mixins
2 | // --------------------------
3 |
4 | .fa-icon() {
5 | display: inline-block;
6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 |
12 | }
13 |
14 | .fa-icon-rotate(@degrees, @rotation) {
15 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
16 | -webkit-transform: rotate(@degrees);
17 | -ms-transform: rotate(@degrees);
18 | transform: rotate(@degrees);
19 | }
20 |
21 | .fa-icon-flip(@horiz, @vert, @rotation) {
22 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1);
23 | -webkit-transform: scale(@horiz, @vert);
24 | -ms-transform: scale(@horiz, @vert);
25 | transform: scale(@horiz, @vert);
26 | }
27 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/path.less:
--------------------------------------------------------------------------------
1 | /* FONT PATH
2 | * -------------------------- */
3 |
4 | @font-face {
5 | font-family: 'FontAwesome';
6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}');
7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'),
8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'),
9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'),
10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'),
11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg');
12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
13 | font-weight: normal;
14 | font-style: normal;
15 | }
16 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/rotated-flipped.less:
--------------------------------------------------------------------------------
1 | // Rotated & Flipped Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); }
5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); }
6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); }
7 |
8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); }
9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); }
10 |
11 | // Hook for IE8-9
12 | // -------------------------
13 |
14 | :root .@{fa-css-prefix}-rotate-90,
15 | :root .@{fa-css-prefix}-rotate-180,
16 | :root .@{fa-css-prefix}-rotate-270,
17 | :root .@{fa-css-prefix}-flip-horizontal,
18 | :root .@{fa-css-prefix}-flip-vertical {
19 | filter: none;
20 | }
21 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/less/stacked.less:
--------------------------------------------------------------------------------
1 | // Stacked Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-stack {
5 | position: relative;
6 | display: inline-block;
7 | width: 2em;
8 | height: 2em;
9 | line-height: 2em;
10 | vertical-align: middle;
11 | }
12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x {
13 | position: absolute;
14 | left: 0;
15 | width: 100%;
16 | text-align: center;
17 | }
18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; }
19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; }
20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; }
21 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/_animated.scss:
--------------------------------------------------------------------------------
1 | // Spinning Icons
2 | // --------------------------
3 |
4 | .#{$fa-css-prefix}-spin {
5 | -webkit-animation: fa-spin 2s infinite linear;
6 | animation: fa-spin 2s infinite linear;
7 | }
8 |
9 | .#{$fa-css-prefix}-pulse {
10 | -webkit-animation: fa-spin 1s infinite steps(8);
11 | animation: fa-spin 1s infinite steps(8);
12 | }
13 |
14 | @-webkit-keyframes fa-spin {
15 | 0% {
16 | -webkit-transform: rotate(0deg);
17 | transform: rotate(0deg);
18 | }
19 | 100% {
20 | -webkit-transform: rotate(359deg);
21 | transform: rotate(359deg);
22 | }
23 | }
24 |
25 | @keyframes fa-spin {
26 | 0% {
27 | -webkit-transform: rotate(0deg);
28 | transform: rotate(0deg);
29 | }
30 | 100% {
31 | -webkit-transform: rotate(359deg);
32 | transform: rotate(359deg);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/_bordered-pulled.scss:
--------------------------------------------------------------------------------
1 | // Bordered & Pulled
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-border {
5 | padding: .2em .25em .15em;
6 | border: solid .08em $fa-border-color;
7 | border-radius: .1em;
8 | }
9 |
10 | .#{$fa-css-prefix}-pull-left { float: left; }
11 | .#{$fa-css-prefix}-pull-right { float: right; }
12 |
13 | .#{$fa-css-prefix} {
14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; }
15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; }
16 | }
17 |
18 | /* Deprecated as of 4.4.0 */
19 | .pull-right { float: right; }
20 | .pull-left { float: left; }
21 |
22 | .#{$fa-css-prefix} {
23 | &.pull-left { margin-right: .3em; }
24 | &.pull-right { margin-left: .3em; }
25 | }
26 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/_core.scss:
--------------------------------------------------------------------------------
1 | // Base Class Definition
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix} {
5 | display: inline-block;
6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/_fixed-width.scss:
--------------------------------------------------------------------------------
1 | // Fixed Width Icons
2 | // -------------------------
3 | .#{$fa-css-prefix}-fw {
4 | width: (18em / 14);
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/_larger.scss:
--------------------------------------------------------------------------------
1 | // Icon Sizes
2 | // -------------------------
3 |
4 | /* makes the font 33% larger relative to the icon container */
5 | .#{$fa-css-prefix}-lg {
6 | font-size: (4em / 3);
7 | line-height: (3em / 4);
8 | vertical-align: -15%;
9 | }
10 | .#{$fa-css-prefix}-2x { font-size: 2em; }
11 | .#{$fa-css-prefix}-3x { font-size: 3em; }
12 | .#{$fa-css-prefix}-4x { font-size: 4em; }
13 | .#{$fa-css-prefix}-5x { font-size: 5em; }
14 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/_list.scss:
--------------------------------------------------------------------------------
1 | // List Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-ul {
5 | padding-left: 0;
6 | margin-left: $fa-li-width;
7 | list-style-type: none;
8 | > li { position: relative; }
9 | }
10 | .#{$fa-css-prefix}-li {
11 | position: absolute;
12 | left: -$fa-li-width;
13 | width: $fa-li-width;
14 | top: (2em / 14);
15 | text-align: center;
16 | &.#{$fa-css-prefix}-lg {
17 | left: -$fa-li-width + (4em / 14);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | // Mixins
2 | // --------------------------
3 |
4 | @mixin fa-icon() {
5 | display: inline-block;
6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 |
12 | }
13 |
14 | @mixin fa-icon-rotate($degrees, $rotation) {
15 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
16 | -webkit-transform: rotate($degrees);
17 | -ms-transform: rotate($degrees);
18 | transform: rotate($degrees);
19 | }
20 |
21 | @mixin fa-icon-flip($horiz, $vert, $rotation) {
22 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
23 | -webkit-transform: scale($horiz, $vert);
24 | -ms-transform: scale($horiz, $vert);
25 | transform: scale($horiz, $vert);
26 | }
27 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/_path.scss:
--------------------------------------------------------------------------------
1 | /* FONT PATH
2 | * -------------------------- */
3 |
4 | @font-face {
5 | font-family: 'FontAwesome';
6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}');
7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'),
8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'),
9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'),
10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'),
11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg');
12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
13 | font-weight: normal;
14 | font-style: normal;
15 | }
16 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/_rotated-flipped.scss:
--------------------------------------------------------------------------------
1 | // Rotated & Flipped Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); }
5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); }
6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); }
7 |
8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); }
9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); }
10 |
11 | // Hook for IE8-9
12 | // -------------------------
13 |
14 | :root .#{$fa-css-prefix}-rotate-90,
15 | :root .#{$fa-css-prefix}-rotate-180,
16 | :root .#{$fa-css-prefix}-rotate-270,
17 | :root .#{$fa-css-prefix}-flip-horizontal,
18 | :root .#{$fa-css-prefix}-flip-vertical {
19 | filter: none;
20 | }
21 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/_stacked.scss:
--------------------------------------------------------------------------------
1 | // Stacked Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-stack {
5 | position: relative;
6 | display: inline-block;
7 | width: 2em;
8 | height: 2em;
9 | line-height: 2em;
10 | vertical-align: middle;
11 | }
12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x {
13 | position: absolute;
14 | left: 0;
15 | width: 100%;
16 | text-align: center;
17 | }
18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; }
19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; }
20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; }
21 |
--------------------------------------------------------------------------------
/public/static/font/font-awesome-4.5.0/scss/font-awesome.scss:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */
5 |
6 | @import "variables";
7 | @import "mixins";
8 | @import "path";
9 | @import "core";
10 | @import "larger";
11 | @import "fixed-width";
12 | @import "list";
13 | @import "bordered-pulled";
14 | @import "animated";
15 | @import "rotated-flipped";
16 | @import "stacked";
17 | @import "icons";
18 |
--------------------------------------------------------------------------------
/public/static/images/anonymous.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/anonymous.jpg
--------------------------------------------------------------------------------
/public/static/images/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/back.png
--------------------------------------------------------------------------------
/public/static/images/back2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/back2.jpg
--------------------------------------------------------------------------------
/public/static/images/back3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/back3.jpg
--------------------------------------------------------------------------------
/public/static/images/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/bg.jpg
--------------------------------------------------------------------------------
/public/static/images/detail1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/detail1.jpg
--------------------------------------------------------------------------------
/public/static/images/details.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/details.jpg
--------------------------------------------------------------------------------
/public/static/images/female.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/female.png
--------------------------------------------------------------------------------
/public/static/images/food.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/food.jpg
--------------------------------------------------------------------------------
/public/static/images/food2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/food2.jpg
--------------------------------------------------------------------------------
/public/static/images/food3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/food3.jpg
--------------------------------------------------------------------------------
/public/static/images/home-header-bg-563ac1688e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/home-header-bg-563ac1688e.png
--------------------------------------------------------------------------------
/public/static/images/male.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/male.png
--------------------------------------------------------------------------------
/public/static/images/topic1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/topic1.jpg
--------------------------------------------------------------------------------
/public/static/images/topic2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/topic2.jpg
--------------------------------------------------------------------------------
/public/static/images/topic3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/topic3.jpg
--------------------------------------------------------------------------------
/public/static/images/topic4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/topic4.jpg
--------------------------------------------------------------------------------
/public/static/images/topic5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/topic5.jpg
--------------------------------------------------------------------------------
/public/static/images/topic6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/topic6.jpg
--------------------------------------------------------------------------------
/public/static/images/topic7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/topic7.jpg
--------------------------------------------------------------------------------
/public/static/images/user.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/public/static/images/user.jpg
--------------------------------------------------------------------------------
/public/static/js/csrf.js:
--------------------------------------------------------------------------------
1 | function getCookie(name) {
2 | var cookieValue = null;
3 | if (document.cookie && document.cookie != '') {
4 | var cookies = document.cookie.split(';');
5 | for (var i = 0; i < cookies.length; i++) {
6 | var cookie = jQuery.trim(cookies[i]);
7 | if (cookie.substring(0, name.length + 1) == (name + '=')) {
8 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
9 | break;
10 | }
11 | }
12 | }
13 | return cookieValue;
14 | }
15 |
16 | var csrftoken = getCookie('csrftoken');
17 |
18 | function csrfSafeMethod(method) {
19 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
20 | }
21 |
22 | $.ajaxSetup({
23 | beforeSend: function(xhr, settings) {
24 | if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
25 | xhr.setRequestHeader("X-CSRFToken", csrftoken);
26 | }
27 | }
28 | });
29 |
--------------------------------------------------------------------------------
/public/static/js/food_detail.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([1],{
2 |
3 | /***/ 0:
4 | /***/ function(module, exports, __webpack_require__) {
5 |
6 | __webpack_require__(1);
7 | module.exports = __webpack_require__(303);
8 |
9 |
10 | /***/ },
11 |
12 | /***/ 303:
13 | /***/ function(module, exports, __webpack_require__) {
14 |
15 | "use strict";
16 |
17 | __webpack_require__(299);
18 | __webpack_require__(304);
19 | window.$ajax = __webpack_require__(302);
20 |
21 | /***/ },
22 |
23 | /***/ 304:
24 | /***/ function(module, exports) {
25 |
26 | // removed by extract-text-webpack-plugin
27 |
28 | /***/ }
29 |
30 | });
--------------------------------------------------------------------------------
/public/static/js/food_explore.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([2],{
2 |
3 | /***/ 0:
4 | /***/ function(module, exports, __webpack_require__) {
5 |
6 | __webpack_require__(1);
7 | module.exports = __webpack_require__(305);
8 |
9 |
10 | /***/ },
11 |
12 | /***/ 305:
13 | /***/ function(module, exports, __webpack_require__) {
14 |
15 | "use strict";
16 |
17 | __webpack_require__(299);
18 | __webpack_require__(306);
19 | window.$ajax = __webpack_require__(302);
20 |
21 | /***/ },
22 |
23 | /***/ 306:
24 | /***/ function(module, exports) {
25 |
26 | // removed by extract-text-webpack-plugin
27 |
28 | /***/ }
29 |
30 | });
--------------------------------------------------------------------------------
/public/static/js/food_list.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([3],{
2 |
3 | /***/ 0:
4 | /***/ function(module, exports, __webpack_require__) {
5 |
6 | __webpack_require__(1);
7 | module.exports = __webpack_require__(307);
8 |
9 |
10 | /***/ },
11 |
12 | /***/ 307:
13 | /***/ function(module, exports, __webpack_require__) {
14 |
15 | "use strict";
16 |
17 | __webpack_require__(299);
18 | __webpack_require__(308);
19 | window.$ajax = __webpack_require__(302);
20 |
21 | /***/ },
22 |
23 | /***/ 308:
24 | /***/ function(module, exports) {
25 |
26 | // removed by extract-text-webpack-plugin
27 |
28 | /***/ }
29 |
30 | });
--------------------------------------------------------------------------------
/public/static/js/home_index.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([4],{
2 |
3 | /***/ 0:
4 | /***/ function(module, exports, __webpack_require__) {
5 |
6 | __webpack_require__(1);
7 | module.exports = __webpack_require__(309);
8 |
9 |
10 | /***/ },
11 |
12 | /***/ 309:
13 | /***/ function(module, exports, __webpack_require__) {
14 |
15 | "use strict";
16 |
17 | __webpack_require__(299);
18 | __webpack_require__(310);
19 | window.$ajax = __webpack_require__(302);
20 | // window.url = "https://api.unsplash.com/photos/random";
21 | // "fa60305aa82e74134cabc7093ef54c8e2c370c47e73152f72371c828daedfcd7"
22 |
23 | (function () {
24 | var container = document.getElementById("container");
25 | var clientHeight = document.documentElement.clientHeight;
26 | var first = true;
27 | container.style.height = clientHeight + "px";
28 |
29 | document.addEventListener("scroll", function () {
30 | if (document.body.scrollTop > clientHeight / 2 && first) {
31 | first = false;
32 | document.getElementById("navbar").className += " over-navbar";
33 | } else if (document.body.scrollTop < clientHeight / 2 - 1 && !first) {
34 | first = true;
35 | document.getElementById("navbar").className = "navbar";
36 | }
37 | }, false);
38 | })();
39 |
40 | /***/ },
41 |
42 | /***/ 310:
43 | /***/ function(module, exports) {
44 |
45 | // removed by extract-text-webpack-plugin
46 |
47 | /***/ }
48 |
49 | });
--------------------------------------------------------------------------------
/public/static/js/js.cookie.js:
--------------------------------------------------------------------------------
1 | !function(e){var n=!1;if("function"==typeof define&&define.amd&&(define(e),n=!0),"object"==typeof exports&&(module.exports=e(),n=!0),!n){var o=window.Cookies,t=window.Cookies=e();t.noConflict=function(){return window.Cookies=o,t}}}(function(){function e(){for(var e=0,n={};e1){if(i=e({path:"/"},t.defaults,i),"number"==typeof i.expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*i.expires),i.expires=a}try{c=JSON.stringify(r),/^[\{\[]/.test(c)&&(r=c)}catch(e){}return r=o.write?o.write(r,n):encodeURIComponent(String(r)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=encodeURIComponent(String(n)),n=n.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),n=n.replace(/[\(\)]/g,escape),document.cookie=[n,"=",r,i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}n||(c={});for(var p=document.cookie?document.cookie.split("; "):[],s=/(%[0-9A-Z]{2})+/g,d=0;d';
52 | };
53 | reader.readAsDataURL(file.files[0]);
54 | } else {
55 | prevDiv.innerHTML = '
';
56 | }
57 | }
58 |
59 | document.getElementById("preview-input").addEventListener("change", preview, false);
60 | };
61 |
62 | /***/ }
63 |
64 | });
--------------------------------------------------------------------------------
/public/static/js/user_register.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([13],{
2 |
3 | /***/ 0:
4 | /***/ function(module, exports, __webpack_require__) {
5 |
6 | __webpack_require__(1);
7 | module.exports = __webpack_require__(329);
8 |
9 |
10 | /***/ },
11 |
12 | /***/ 324:
13 | /***/ function(module, exports) {
14 |
15 | // removed by extract-text-webpack-plugin
16 |
17 | /***/ },
18 |
19 | /***/ 325:
20 | /***/ function(module, exports) {
21 |
22 | "use strict";
23 |
24 | module.exports = function () {
25 | var loginContainer = document.getElementById("form-container");
26 | var inputs = loginContainer.getElementsByTagName("input");
27 |
28 | for (var idx = 0; idx < inputs.length; idx++) {
29 | inputs[idx].addEventListener("focus", function (event) {
30 | var e = window.event || event;
31 | var target = e.srcElement || e.target;
32 | if (target.name === "button") {
33 | return;
34 | }
35 | var label = target.parentNode.firstElementChild;
36 | label.className = "focus-label";
37 | }, false);
38 |
39 | inputs[idx].addEventListener("blur", function (event) {
40 | var e = window.event || event;
41 | var target = e.srcElement || e.target;
42 | if (target.value === "") {
43 | var label = target.parentNode.firstElementChild;
44 | label.className = "";
45 | target.className = "";
46 | } else {
47 | target.className = "compvare-input";
48 | }
49 | }, false);
50 | }
51 | };
52 |
53 | /***/ },
54 |
55 | /***/ 329:
56 | /***/ function(module, exports, __webpack_require__) {
57 |
58 | "use strict";
59 |
60 | __webpack_require__(299);
61 | __webpack_require__(324);
62 | var formAnimate = __webpack_require__(325);
63 | formAnimate();
64 | window.$ajax = __webpack_require__(302);
65 |
66 | /***/ }
67 |
68 | });
--------------------------------------------------------------------------------
/public/static/js/user_settings.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([14],{
2 |
3 | /***/ 0:
4 | /***/ function(module, exports, __webpack_require__) {
5 |
6 | __webpack_require__(1);
7 | module.exports = __webpack_require__(330);
8 |
9 |
10 | /***/ },
11 |
12 | /***/ 316:
13 | /***/ function(module, exports) {
14 |
15 | // removed by extract-text-webpack-plugin
16 |
17 | /***/ },
18 |
19 | /***/ 327:
20 | /***/ function(module, exports) {
21 |
22 | // removed by extract-text-webpack-plugin
23 |
24 | /***/ },
25 |
26 | /***/ 328:
27 | /***/ function(module, exports) {
28 |
29 | 'use strict';
30 |
31 | module.exports = function () {
32 | function preview(file) {
33 | var prevDiv = document.getElementById('preview-span');
34 | var file = this;
35 | if (file.files && file.files[0]) {
36 | var reader = new FileReader();
37 | reader.onload = function (evt) {
38 | prevDiv.innerHTML = '
';
39 | };
40 | reader.readAsDataURL(file.files[0]);
41 | } else {
42 | prevDiv.innerHTML = '
';
43 | }
44 | }
45 |
46 | document.getElementById("preview-input").addEventListener("change", preview, false);
47 | };
48 |
49 | /***/ },
50 |
51 | /***/ 330:
52 | /***/ function(module, exports, __webpack_require__) {
53 |
54 | "use strict";
55 |
56 | __webpack_require__(299);
57 | __webpack_require__(316);
58 | __webpack_require__(327);
59 | __webpack_require__(328)();
60 | window.$ajax = __webpack_require__(302);
61 |
62 | /***/ }
63 |
64 | });
--------------------------------------------------------------------------------
/public/static/js/user_share.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([15],{
2 |
3 | /***/ 0:
4 | /***/ function(module, exports, __webpack_require__) {
5 |
6 | __webpack_require__(1);
7 | module.exports = __webpack_require__(331);
8 |
9 |
10 | /***/ },
11 |
12 | /***/ 316:
13 | /***/ function(module, exports) {
14 |
15 | // removed by extract-text-webpack-plugin
16 |
17 | /***/ },
18 |
19 | /***/ 331:
20 | /***/ function(module, exports, __webpack_require__) {
21 |
22 | "use strict";
23 |
24 | __webpack_require__(299);
25 | __webpack_require__(316);
26 | __webpack_require__(332);
27 | window.$ajax = __webpack_require__(302);
28 |
29 | /***/ },
30 |
31 | /***/ 332:
32 | /***/ function(module, exports) {
33 |
34 | // removed by extract-text-webpack-plugin
35 |
36 | /***/ }
37 |
38 | });
--------------------------------------------------------------------------------
/public/static/js/user_topic_collection.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([16],{
2 |
3 | /***/ 0:
4 | /***/ function(module, exports, __webpack_require__) {
5 |
6 | __webpack_require__(1);
7 | module.exports = __webpack_require__(333);
8 |
9 |
10 | /***/ },
11 |
12 | /***/ 316:
13 | /***/ function(module, exports) {
14 |
15 | // removed by extract-text-webpack-plugin
16 |
17 | /***/ },
18 |
19 | /***/ 333:
20 | /***/ function(module, exports, __webpack_require__) {
21 |
22 | "use strict";
23 |
24 | __webpack_require__(299);
25 | __webpack_require__(316);
26 | __webpack_require__(334);
27 | window.$ajax = __webpack_require__(302);
28 |
29 | /***/ },
30 |
31 | /***/ 334:
32 | /***/ function(module, exports) {
33 |
34 | // removed by extract-text-webpack-plugin
35 |
36 | /***/ }
37 |
38 | });
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | amqp==1.4.9
2 | anyjson==0.3.3
3 | billiard==3.3.0.23
4 | bleach==1.4.3
5 | celery==3.1.23
6 | Django==1.9.6
7 | django-celery==3.1.17
8 | django-haystack==2.4.1
9 | django-redis==4.4.2
10 | django-taggit==0.18.2
11 | django-updown==1.0.1
12 | django-widget-tweaks==1.4.1
13 | gunicorn==19.5.0
14 | html5lib==0.9999999
15 | kombu==3.0.35
16 | mysqlclient==1.3.7
17 | Pillow==3.3.1
18 | pysolr==3.4.0
19 | pytz==2016.4
20 | redis==2.10.5
21 | requests==2.10.0
22 | six==1.10.0
23 | sorl-thumbnail==12.3
24 |
--------------------------------------------------------------------------------
/run_celery.sh:
--------------------------------------------------------------------------------
1 | python3 manage.py celery worker --loglevel=info
--------------------------------------------------------------------------------
/scripts/deploy.sh:
--------------------------------------------------------------------------------
1 | cd /code/pineapple
2 | git reset --hard HEAD
3 | git pull -f
4 | if [ -d "venv" ]; then
5 | echo "**> virtualenv exists"
6 | else
7 | echo "**> creating virtualenv"
8 | virtualenv venv
9 | fi
10 | . venv/bin/activate
11 | pip install -r requirements.txt
12 | python manage.py makemigrations
13 | python manage.py migrate
14 | python manage.py test
15 | python manage.py collectstatic
16 | python manage.py load_fixtures
17 | BUILD_ID=dontKillMe nohup gunicorn pineapple.wsgi:application -c conf/gunicorn_conf.py&
--------------------------------------------------------------------------------
/search/context_processors.py:
--------------------------------------------------------------------------------
1 |
2 | from .forms import SearchForm
3 |
4 | def search_proc(request):
5 | form = SearchForm()
6 | return {
7 | 'search_form': form
8 | }
--------------------------------------------------------------------------------
/search/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 | class SearchForm(forms.Form):
4 | q = forms.CharField()
5 |
--------------------------------------------------------------------------------
/search/index.py:
--------------------------------------------------------------------------------
1 | from haystack import indexes
2 |
3 | from food.models import Food
4 |
5 |
6 | class FoodIndex(indexes.SearchIndex, indexes.Indexable):
7 | text = indexes.CharField(document=True, use_template=True)
8 | title = indexes.CharField(model_attr='title')
9 | description = indexes.TextField(model_attr='description')
10 |
11 | def get_model(self):
12 | return Food
13 |
14 | def index_queryset(self, using=None):
15 | return self.get_model().objects.all()
16 |
--------------------------------------------------------------------------------
/search/templates/search/search.tpl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/search/templates/search/search.tpl
--------------------------------------------------------------------------------
/search/test.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/search/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from .views import search
4 |
5 | urlpatterns = [
6 | url(r'', search, name='search'),
7 | ]
--------------------------------------------------------------------------------
/search/views.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.http import HttpResponse, HttpResponseRedirect
3 | from django.shortcuts import render
4 |
5 | from .forms import SearchForm
6 |
7 | from food.models import Food
8 |
9 | from haystack.query import SearchQuerySet
10 |
11 | categorys = Food.get_food_categorys()
12 |
13 | def search(request):
14 | form = SearchForm(request.POST)
15 | if form.is_valid():
16 | cd = form.cleaned_data
17 | if settings.FULLTEXT_SEARCH is True:
18 | foods = SearchQuerySet().models(Food).filter(content=cd['q']).load_all()
19 | total_results = foods.count()
20 | else:
21 | foods = Food.objects.filter(title__contains=cd['q'])
22 | return render(request, 'food/list.tpl', {
23 | 'foods': foods,
24 | 'categorys': categorys
25 | })
26 | return HttpResponse(status=400)
27 |
28 |
--------------------------------------------------------------------------------
/topic/__init__.py:
--------------------------------------------------------------------------------
1 | default_app_config = 'topic.apps.TopicConfig'
--------------------------------------------------------------------------------
/topic/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.core.urlresolvers import reverse
3 | from django.utils.html import format_html
4 |
5 | from comments.models import TopicComment
6 | from .models import FoodTopic
7 |
8 | # Register your models here.
9 | class TopicCommentAdmin(admin.TabularInline):
10 | model = TopicComment
11 |
12 |
13 | class FoodTopicAdmin(admin.ModelAdmin):
14 | model = FoodTopic
15 |
16 | list_display = ('title', 'show_firm_url')
17 |
18 | def show_firm_url(self, obj):
19 | return format_html("{url}", url=reverse('topic:detail', kwargs={'topic_id': obj.id}))
20 |
21 | show_firm_url.short_description = '地址'
22 |
23 | inlines = [TopicCommentAdmin]
24 |
25 |
26 | admin.site.register(FoodTopic, FoodTopicAdmin)
27 |
--------------------------------------------------------------------------------
/topic/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class TopicConfig(AppConfig):
5 | name = 'topic'
6 | verbose_name = '专题'
7 |
--------------------------------------------------------------------------------
/topic/food_list.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'user/base.html' %}
2 | {% load staticfiles %}
3 | {% block head %}
4 |
5 | {% endblock head %}
6 | {% block content %}
7 |
8 | {% endblock content %}
9 | {% block js %}
10 | {% endblock js %}
--------------------------------------------------------------------------------
/topic/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 | from .models import FoodTopic
4 |
5 | class FoodTopicForm(forms.ModelForm):
6 |
7 | class Meta:
8 | model = FoodTopic
9 |
10 |
--------------------------------------------------------------------------------
/topic/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-13 05:09
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | ('comments', '0001_initial'),
15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 | ('food', '0001_initial'),
17 | ]
18 |
19 | operations = [
20 | migrations.CreateModel(
21 | name='FoodTopic',
22 | fields=[
23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
24 | ('title', models.CharField(max_length=32, verbose_name='专题名称')),
25 | ('comments', models.ManyToManyField(blank=True, to='comments.Comment', verbose_name='专题评论')),
26 | ('foods', models.ManyToManyField(to='food.Food', verbose_name='美食')),
27 | ('users_collect', models.ManyToManyField(blank=True, related_name='topics_collected', to=settings.AUTH_USER_MODEL, verbose_name='收藏的用户')),
28 | ('users_like', models.ManyToManyField(blank=True, related_name='topics_liked', to=settings.AUTH_USER_MODEL, verbose_name='点赞用户')),
29 | ],
30 | options={
31 | 'verbose_name': '美食专题',
32 | 'verbose_name_plural': '美食专题',
33 | },
34 | ),
35 | ]
36 |
--------------------------------------------------------------------------------
/topic/migrations/0002_auto_20160514_0738.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-14 07:38
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 | import taggit.managers
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('taggit', '0002_auto_20150616_2121'),
13 | ('topic', '0001_initial'),
14 | ]
15 |
16 | operations = [
17 | migrations.RemoveField(
18 | model_name='foodtopic',
19 | name='comments',
20 | ),
21 | migrations.AddField(
22 | model_name='foodtopic',
23 | name='tags',
24 | field=taggit.managers.TaggableManager(blank=True, help_text='多个标签以逗号分隔', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='标签'),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/topic/migrations/0003_auto_20160515_0738.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-15 07:38
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('topic', '0002_auto_20160514_0738'),
12 | ]
13 |
14 | operations = [
15 | migrations.RemoveField(
16 | model_name='foodtopic',
17 | name='users_like',
18 | ),
19 | migrations.AlterField(
20 | model_name='foodtopic',
21 | name='foods',
22 | field=models.ManyToManyField(related_name='topics_belong', to='food.Food', verbose_name='美食'),
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/topic/migrations/0004_foodtopic_total_collects.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-22 12:30
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('topic', '0003_auto_20160515_0738'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='foodtopic',
17 | name='total_collects',
18 | field=models.PositiveIntegerField(db_index=True, default=0, verbose_name='收藏数'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/topic/migrations/0005_foodtopic_cover_image.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-25 12:54
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('topic', '0004_foodtopic_total_collects'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='foodtopic',
17 | name='cover_image',
18 | field=models.ImageField(default='qwe', upload_to='topic/cover/%Y/%m/%d', verbose_name='封面图片'),
19 | preserve_default=False,
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/topic/migrations/0006_foodtopic_description.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-25 12:57
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('topic', '0005_foodtopic_cover_image'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='foodtopic',
17 | name='description',
18 | field=models.TextField(default='123', verbose_name='专题描述'),
19 | preserve_default=False,
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/topic/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/topic/migrations/__init__.py
--------------------------------------------------------------------------------
/topic/models.py:
--------------------------------------------------------------------------------
1 | from django.core.urlresolvers import reverse
2 | from django.db import models
3 |
4 | from food.models import Food
5 | from taggit.managers import TaggableManager
6 | from user.models import User
7 |
8 | # Create your models here.
9 | class FoodTopic(models.Model):
10 | title = models.CharField(max_length=32, verbose_name='专题名称')
11 | cover_image = models.ImageField(upload_to='topic/cover/%Y/%m/%d', verbose_name='封面图片')
12 | description = models.TextField(verbose_name='专题描述')
13 | users_collect = models.ManyToManyField(User, related_name='topics_collected', blank=True, verbose_name='收藏的用户')
14 | total_collects = models.PositiveIntegerField(db_index=True, default=0, verbose_name='收藏数')
15 | foods = models.ManyToManyField(Food, related_name='topics_belong', verbose_name='美食')
16 | tags = TaggableManager(blank=True, help_text='多个标签以逗号分隔', verbose_name="标签")
17 |
18 | def __str__(self):
19 | return self.title
20 |
21 | def get_absolute_url(self):
22 | return reverse('topic:detail', kwargs={'topic_id': self.id})
23 |
24 | class Meta:
25 | verbose_name = '美食专题'
26 | verbose_name_plural = '美食专题'
27 |
--------------------------------------------------------------------------------
/topic/templates/topic/list.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'home/base.tpl' %}
2 | {% load staticfiles %}
3 | {% load thumbnail %}
4 | {% block head %}
5 | 专题
6 |
7 | {% endblock head %}
8 | {% block content %}
9 |
10 |
11 | {% for topic in topics %}
12 |
28 | {% endfor %}
29 |
30 |
31 | {% endblock content %}
32 | {% block js %}
33 |
34 | {% endblock js %}
35 |
--------------------------------------------------------------------------------
/topic/templates/topic/list_ajax.tpl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/topic/templates/topic/list_ajax.tpl
--------------------------------------------------------------------------------
/topic/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/topic/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from .views import topic_list, topic_detail, topic_collect
4 |
5 | urlpatterns = [
6 | url(r'^$', topic_list, name='list'),
7 | url(r'^(?P[\d]+)/$', topic_detail, name='detail'),
8 | url(r'^collect/$', topic_collect, name='collect'),
9 | ]
--------------------------------------------------------------------------------
/topic/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.decorators import login_required
2 | from django.http import JsonResponse
3 | from django.shortcuts import render, get_object_or_404
4 | from django.views.decorators.http import require_POST
5 |
6 | from .models import FoodTopic
7 |
8 | from actions.utils import create_action
9 | from constants import *
10 | from utils import make_paginator
11 | from utils.decorators import ajax_required
12 |
13 | # 专题列表
14 | def topic_list(request):
15 | topics = make_paginator(request, FoodTopic.objects.all())
16 | if request.is_ajax():
17 | return render(request, 'topic/list_ajax.tpl',
18 | {'topics': topics})
19 | return render(request, 'topic/list.tpl', {
20 | 'topics': topics
21 | })
22 |
23 | # 专题详情
24 | def topic_detail(request, topic_id):
25 | topic = get_object_or_404(FoodTopic, pk=topic_id)
26 | foods = make_paginator(request, topic.foods.all())
27 | return render(request, 'topic/detail.tpl', {
28 | 'topic': topic,
29 | 'foods': foods
30 | })
31 |
32 | # 收藏
33 | @login_required
34 | @require_POST
35 | @ajax_required
36 | def topic_collect(request):
37 | topic_id = request.POST.get('id')
38 | action = request.POST.get('action')
39 | if topic_id and action:
40 | topic = FoodTopic.objects.get(pk=topic_id)
41 | if action == 'collect':
42 | topic.users_collect.add(request.user)
43 | topic.total_collects += 1
44 | create_action(request.user, COLLECT, topic)
45 | elif action == 'uncollect':
46 | topic.users_collect.remove(request.user)
47 | topic.total_collects -= 1
48 | else:
49 | return JsonResponse(JSON_FAIL(STATUS_INVALID_ARGUMENTS))
50 | topic.save()
51 | return JsonResponse(JSON_SUCCESS)
52 | return JsonResponse(JSON_FAIL(STATUS_INVALID_ARGUMENTS), status=400)
53 |
--------------------------------------------------------------------------------
/user/__init__.py:
--------------------------------------------------------------------------------
1 | default_app_config = 'user.apps.UserConfig'
2 |
--------------------------------------------------------------------------------
/user/admin.py:
--------------------------------------------------------------------------------
1 | from django.core.urlresolvers import reverse
2 | from django.contrib import admin
3 | from django.contrib.admin import AdminSite
4 | from django.contrib.auth.admin import UserAdmin
5 | from django.utils.html import format_html
6 |
7 | from .models import User, UserProfile, UserSetting
8 |
9 | # Register your models here.
10 | class UserProfileAdmin(admin.TabularInline):
11 | model = UserProfile
12 |
13 |
14 | class UserSettingAdmin(admin.TabularInline):
15 | model = UserSetting
16 |
17 |
18 | class UserAdmin(UserAdmin):
19 | fieldsets = (
20 | ('个人信息', {'fields': ('username', 'email', 'password')}),
21 | ('权限', {'fields': ('is_active', 'is_staff', 'is_superuser')}),
22 | ('登录信息', {'fields': ('date_joined', 'last_login',)}),
23 | )
24 | list_display = ('username', 'email', 'is_active', 'is_staff', 'is_superuser')
25 | ordering = ('date_joined',)
26 |
27 | inlines = [
28 | UserProfileAdmin,
29 | UserSettingAdmin
30 | ]
31 |
32 |
33 | admin.site.register(User, UserAdmin)
34 |
--------------------------------------------------------------------------------
/user/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class UserConfig(AppConfig):
5 | name = 'user'
6 | verbose_name = '用户'
7 |
--------------------------------------------------------------------------------
/user/auth.py:
--------------------------------------------------------------------------------
1 | from .models import User
2 |
3 |
4 | class EmailAuthBackend(object):
5 | def authenticate(self, username=None, password=None):
6 | try:
7 | user = User.objects.get(email=username)
8 | if user.check_password(password):
9 | return user
10 | return None
11 | except User.DoesNotExist:
12 | return None
13 |
14 | def get_user(self, user_id):
15 | try:
16 | return User.objects.get(pk=user_id)
17 | except User.DoesNotExist:
18 | return None
--------------------------------------------------------------------------------
/user/middlewares.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/user/middlewares.py
--------------------------------------------------------------------------------
/user/migrations/0002_auto_20160513_0509.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-13 05:09
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ('user', '0001_initial'),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterField(
18 | model_name='usersetting',
19 | name='user',
20 | field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='settings', to=settings.AUTH_USER_MODEL),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/user/migrations/0003_usersetting_background_img.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-14 08:13
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('user', '0002_auto_20160513_0509'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='usersetting',
17 | name='background_img',
18 | field=models.ImageField(blank=True, upload_to='users/background/%Y/%m/%d', verbose_name='主页背景'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/user/migrations/0004_auto_20160515_0738.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-05-15 07:38
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('user', '0003_usersetting_background_img'),
13 | ]
14 |
15 | operations = [
16 | migrations.RenameField(
17 | model_name='userprofile',
18 | old_name='sex',
19 | new_name='gender',
20 | ),
21 | migrations.AlterField(
22 | model_name='user',
23 | name='following',
24 | field=models.ManyToManyField(blank=True, related_name='followers', to=settings.AUTH_USER_MODEL),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/user/migrations/0005_auto_20160604_0849.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2016-06-04 08:49
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('user', '0004_auto_20160515_0738'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='userprofile',
17 | name='introduction',
18 | field=models.TextField(blank=True, max_length=1024, verbose_name='个人简介'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/user/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/user/migrations/__init__.py
--------------------------------------------------------------------------------
/user/models.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import AbstractUser
2 | from django.core.urlresolvers import reverse
3 | from django.db import models
4 | from django.db.models.signals import post_save
5 | from django.dispatch import receiver
6 |
7 | from location.models import City
8 |
9 | # Create your models here.
10 | class User(AbstractUser):
11 | following = models.ManyToManyField('self', blank=True, related_name='followers', symmetrical=False)
12 |
13 | def __str__(self):
14 | return self.username
15 |
16 | def get_absolute_url(self):
17 | return reverse('user:home', kwargs={'user_id': self.id})
18 |
19 |
20 | class UserSetting(models.Model):
21 | user = models.OneToOneField(User, related_name='settings', on_delete=models.CASCADE)
22 | background_img = models.ImageField(upload_to='users/background/%Y/%m/%d', blank=True, verbose_name='主页背景')
23 |
24 | def __str__(self):
25 | return "{}的设置".format(self.user.username)
26 |
27 | class Meta:
28 | verbose_name = '用户设置'
29 | verbose_name_plural = '用户设置'
30 |
31 |
32 | class UserProfile(models.Model):
33 | user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE)
34 | location = models.ForeignKey(City, null=True, blank=True, verbose_name='居住城市')
35 | introduction = models.TextField(max_length=1024, blank=True, verbose_name='个人简介')
36 | gender = models.CharField(max_length=2, choices=[('m', '男'), ('f', '女')], null=True, blank=True, verbose_name='性别')
37 | date_of_birth = models.DateField(blank=True, null=True, verbose_name='出生日期')
38 | avatar = models.ImageField(upload_to='users/avatar/%Y/%m/%d', blank=True, verbose_name='头像')
39 |
40 | def __str__(self):
41 | return '{}的档案'.format(self.user.username)
42 |
43 | class Meta:
44 | verbose_name = '用户档案'
45 | verbose_name_plural = '用户档案'
46 |
47 |
48 | @receiver(post_save, sender=User)
49 | def user_post_save_handler(sender, instance, created, **kwargs):
50 | if created is True and not kwargs.get('raw', False):
51 | UserSetting.objects.create(user=instance)
52 | UserProfile.objects.create(user=instance)
53 | instance.save()
54 |
--------------------------------------------------------------------------------
/user/tasks.py:
--------------------------------------------------------------------------------
1 | from celery import task
2 | from django.core import signing
3 | from django.core.urlresolvers import reverse
4 | from django.conf import settings
5 | from django.core.mail import send_mail
6 | from django.template.loader import render_to_string
7 |
8 | @task
9 | def confirm_user(id, username):
10 | token = signing.dumps({'user_id': id})
11 | url = reverse('user:confirm', kwargs={'token': token})
12 | subject = '{}, 激活你的帐户'.format(username)
13 | message = render_to_string('user/confirm.tpl', {'url': url})
14 | mail_sent = send_mail(subject, message, settings.EMAIL_HOST_USER, ['493632323@qq.com'])
15 | return mail_sent
16 |
--------------------------------------------------------------------------------
/user/templates/user/chat.tpl:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/user/templates/user/confirm.tpl:
--------------------------------------------------------------------------------
1 | 请点击下面的链接确认你的帐户:{{ url }}
2 |
--------------------------------------------------------------------------------
/user/templates/user/food_list.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'user/base.tpl' %}
2 | {% load staticfiles %}
3 | {% block head %}
4 | 想吃的
5 |
6 | {% endblock head %}
7 | {% block content %}
8 |
9 |
10 | {% for food in foods %}
11 |
12 |
13 |
14 |
15 |
16 | {{ food.description }}
17 |
18 |
19 |
{{ food.rating.likes }}
20 |
21 |
22 |
23 |
24 | {% endfor %}
25 |
26 |
27 | {% endblock content %}
28 | {% block js %}
29 |
30 | {% endblock js %}
--------------------------------------------------------------------------------
/user/templates/user/index.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'user/base.tpl' %}
2 | {% load staticfiles %}
3 | {% load humanize %}
4 | {% block head %}
5 | 用户首页
6 |
7 |
13 | {% endblock head %}
14 | {% block content %}
15 |
16 |
17 | {% if actions %}
18 |
19 | {% for action in actions %}
20 |
21 |
22 |
{{ action.user }}
23 |
{{ action.verb }}
24 |
{{ action.target }}
25 |
{{ action.created | naturaltime}}
26 |
27 | {% endfor %}
28 |
29 | {% else %}
30 |
ta最近还没有个人动态
31 | {% endif %}
32 |
33 |
34 | {% endblock content %}
35 | {% block js %}
36 |
37 | {% endblock js %}
--------------------------------------------------------------------------------
/user/templates/user/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 登录
6 |
7 |
8 |
9 |
10 |
11 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
登录
33 | 登录,寻找更好的
34 |
35 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/user/templates/user/login.tpl:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 | {% load widget_tweaks %}
3 |
4 |
5 |
6 |
7 | 登录 - Pineapple
8 |
9 |
10 |
23 |
24 |
25 | {% include 'home/navbar.tpl' %}
26 |
27 |
28 |
29 |
登录
30 | 登录,一起发现有趣的事物
31 |
32 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/user/templates/user/moments.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'user/base.tpl' %}
2 | {% load staticfiles %}
3 | {% load humanize %}
4 | {% block head %}
5 | 吃友圈
6 |
7 |
13 | {% endblock head %}
14 | {% block content %}
15 |
16 |
17 | {% if actions %}
18 |
19 | {% for action in actions %}
20 |
21 |
22 |
{{ action.user }}
23 |
{{ action.verb }}
24 |
{{ action.target }}
25 |
{{ action.created | naturaltime}}
26 |
27 | {% endfor %}
28 |
29 | {% else %}
30 |
ta最近还没有个人动态
31 | {% endif %}
32 |
33 |
34 | {% endblock content %}
35 | {% block js %}
36 |
37 | {% endblock js %}
--------------------------------------------------------------------------------
/user/templates/user/settings.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'user/base.tpl' %}
2 | {% load staticfiles %}
3 | {% block head %}
4 |
5 | {% endblock head %}
6 | {% block content %}
7 |
28 | {% endblock content %}
29 | {% block js %}
30 |
31 | {% endblock js %}
--------------------------------------------------------------------------------
/user/templates/user/share.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'user/base.tpl' %}
2 | {% load staticfiles %}
3 | {% load humanize %}
4 | {% block head %}
5 |
6 | {% endblock head %}
7 | {% block content %}
8 |
29 | {% endblock content %}
30 | {% block js %}
31 |
32 | {% endblock js %}
33 |
--------------------------------------------------------------------------------
/user/templates/user/topic_collection.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'user/base.tpl' %}
2 | {% load staticfiles %}
3 | {% block head %}
4 |
5 | {% endblock head %}
6 | {% block content %}
7 |
8 |
9 | {% for topic in collections %}
10 |
11 |
14 |
17 |
18 | {{ topic.total_collects }}
19 |
20 |
21 | {% endfor %}
22 |
23 |
24 | {% endblock content %}
25 | {% block js %}
26 |
27 | {% endblock js %}
28 |
--------------------------------------------------------------------------------
/user/templates/user/user_list.tpl:
--------------------------------------------------------------------------------
1 | {% extends 'user/base.tpl' %}
2 | {% load staticfiles %}
3 | {% load thumbnail %}
4 | {% block head %}
5 |
6 | {% endblock head %}
7 | {% block content %}
8 |
9 |
10 | {% if users %}
11 | {% for user in users %}
12 |
23 | {% endfor %}
24 | {% else %}
25 | {% if request.TAB == 'following' %}
26 |
ta还没关注任何人
27 | {% else %}
28 |
ta还没被任何人关注
29 | {% endif %}
30 | {% endif %}
31 |
32 |
33 | {% endblock content %}
34 | {% block js %}
35 |
36 | {% endblock js %}
37 |
38 |
--------------------------------------------------------------------------------
/user/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 | from django.contrib.auth.views import (password_change, password_change_done,
3 | password_reset, password_reset_done, password_reset_complete, password_reset_confirm)
4 |
5 | from .views import (home, get_user_id, user_login, user_logout, user_register, user_follow, user_followers, user_following, user_posts,
6 | user_confirm, user_settings, user_profile, user_moments, user_shared, user_wants_to_eat, user_ate, topics_collection, get_cities)
7 |
8 | urlpatterns = [
9 | url(r'^(?P[\d]+)/$', home, name='home'),
10 | url(r'^whoami/$', get_user_id, name='userid'),
11 |
12 | url(r'^login/$', user_login, name='login'),
13 | url(r'^logout/$', user_logout, name='logout'),
14 | url(r'^register/$', user_register, name='register'),
15 | url(r'^confirm/(?P.+)/$', user_confirm, name='confirm'),
16 |
17 | url(r'^password-change/$', password_change, name='password_change'),
18 | url(r'^password-change/done/$', password_change_done, name='password_change_done'),
19 |
20 | url(r'^password-reset/$', password_reset, name='password_reset'),
21 | url(r'^password-reset/done/$', password_reset_done, name='password_reset_done'),
22 | url(r'^password-reset/confirm/(?P[-\w]+)/(?P[-\w]+)/$', password_reset_confirm, name='password_reset_confirm'),
23 | url(r'^password-reset/complete/$', password_reset_complete, name='password_reset_complete'),
24 |
25 | url(r'^settings/$', user_settings, name='settings'),
26 | url(r'^profile/$', user_profile, name='profile'),
27 | url(r'^moments/$', user_moments, name='moments'),
28 |
29 | url(r'^(?P[\d]+)/shared/$', user_shared, name='share'),
30 | url(r'^(?P[\d]+)/following/$', user_following, name='following'),
31 | url(r'^(?P[\d]+)/followers/$', user_followers, name='followers'),
32 | url(r'^(?P[\d]+)/wta/$', user_wants_to_eat, name='wta'),
33 | url(r'^(?P[\d]+)/ate/$', user_ate, name='ate'),
34 | url(r'^(?P[\d]+)/posts/$', user_posts, name='posts'),
35 | url(r'^(?P[\d]+)/topic-collection', topics_collection, name='topic-collection'),
36 |
37 | url(r'^follow/$', user_follow, name='follow'),
38 | url(r'^get_cities/$', get_cities, name='get_cities'),
39 | ]
--------------------------------------------------------------------------------
/utils/__init__.py:
--------------------------------------------------------------------------------
1 | #-*- coding:utf-8 -*-
2 | from django.core.paginator import Paginator, InvalidPage, EmptyPage
3 | from django.core.cache import cache
4 |
5 | # Create your views here.
6 | def make_paginator(request, items, per_page=10, cache_key=None, cache_time=0, cahce_pages=10):
7 | """Make paginator."""
8 | try:
9 | page = int(request.GET.get("page", '1'))
10 | except ValueError:
11 | page = 1
12 |
13 | if cache_key:
14 | _items = cache.get('{}:{}'.format(cache_key, page))
15 | if _items:
16 | return _items
17 |
18 | paginator = Paginator(items, per_page)
19 | try:
20 | items = paginator.page(page)
21 | except (InvalidPage, EmptyPage):
22 | items = paginator.page(paginator.num_pages)
23 |
24 | if cache_key and page < cahce_pages:
25 | # 缓存分页
26 | cache.set('{}:{}'.format(cache_key, page), items, cache_time)
27 | return items
--------------------------------------------------------------------------------
/utils/decorators.py:
--------------------------------------------------------------------------------
1 | from functools import wraps
2 | from django.http import HttpResponseBadRequest
3 |
4 | def ajax_required(view):
5 | @wraps(view)
6 | def wrapper(request, *args, **kws):
7 | if not request.is_ajax():
8 | return HttpResponseBadRequest()
9 | return view(request, *args, **kws)
10 | return wrapper
11 |
12 | def tab(tab, *, sub_tab=None):
13 | def wrapper(view):
14 | @wraps(view)
15 | def _wrapper(request, *args, **kws):
16 | request.TAB = tab
17 | request.SUBTAB = sub_tab
18 | return view(request, *args, **kws)
19 | return _wrapper
20 | return wrapper
--------------------------------------------------------------------------------
/utils/form.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.core.validators import RegexValidator
3 |
4 | def RegexPassWordField(label):
5 | return forms.CharField(label=label,
6 | validators=[RegexValidator('^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d#?!@$%^&*+-]{6,32}$',
7 | message="密码长度不少于6且至少包含一个字母和一个数字")],
8 | widget=forms.PasswordInput)
--------------------------------------------------------------------------------
/utils/mixins.py:
--------------------------------------------------------------------------------
1 | #-*- coding:utf-8 -*-
2 | import bleach
3 | from django.conf import settings
4 |
5 | class CleanContentMixin:
6 | # 防御富文本编辑器造成的XSS注入
7 | def clean_content(self):
8 | content = self.cleaned_data.get('content', '')
9 | cleaned_text = bleach.clean(content, settings.BLEACH_VALID_TAGS, settings.BLEACH_VALID_ATTRS, settings.BLEACH_VALID_STYLES)
10 | return cleaned_text
--------------------------------------------------------------------------------
/utils/taggit.py:
--------------------------------------------------------------------------------
1 |
2 | def comma_splitter(tag_string):
3 | tag_string = tag_string.replace(' ', '').replace(',', ',')
4 | return [t.strip().lower() for t in tag_string.split(',') if t.strip()]
5 |
--------------------------------------------------------------------------------
/utils/templatetag/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pineapplers/pineapple/dfc8a6772964c96684a9817df9b0970595ea18f2/utils/templatetag/__init__.py
--------------------------------------------------------------------------------
/utils/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------