瀏覽代碼

Feature: Add pin-creation from remote-url

+ Add CSRF protection for every request
pull/169/head
winkidney 5 年之前
committed by Isaac Bythewood
父節點
當前提交
bb24ed3ecf
共有 9 個檔案被更改,包括 279 行新增24 行删除
  1. +1
    -0
      pinry-spa/public/index.html
  2. +13
    -24
      pinry-spa/src/components/PHeader.vue
  3. +12
    -0
      pinry-spa/src/components/api.js
  4. +45
    -0
      pinry-spa/src/components/modals.js
  5. +63
    -0
      pinry-spa/src/components/pin_create/FileUpload.vue
  6. +114
    -0
      pinry-spa/src/components/pin_create/PinCreateModal.vue
  7. +17
    -0
      pinry-spa/src/views/PinCreate.vue
  8. +11
    -0
      pinry/middleware.py
  9. +3
    -0
      pinry/settings/base.py

+ 1
- 0
pinry-spa/public/index.html 查看文件

@@ -13,5 +13,6 @@
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<link rel="stylesheet" href="https://cdn.materialdesignicons.com/2.5.94/css/materialdesignicons.min.css">
</body>
</html>

+ 13
- 24
pinry-spa/src/components/PHeader.vue 查看文件

@@ -27,16 +27,15 @@
Create
</a>
<div class="navbar-dropdown">
<router-link
to="/"
<a
@click="createPin"
class="navbar-item">
Pin
</router-link>
<router-link
to="/"
</a>
<a
class="navbar-item">
Board
</router-link>
</a>
</div>
</div>
<div
@@ -104,8 +103,7 @@

<script>
import api from './api';
import LoginForm from './LoginForm.vue';
import SignUpForm from './SignUpForm.vue';
import modals from './modals';

export default {
name: 'p-header',
@@ -128,6 +126,8 @@ export default {
onSignUpSucceed() {
this.initializeUser(true);
},
onPinCreated() {
},
logOut() {
api.User.logOut().then(
() => {
@@ -136,24 +136,13 @@ export default {
);
},
logIn() {
this.$buefy.modal.open({
parent: this,
component: LoginForm,
hasModalCard: true,
events: {
'login.succeed': this.onLoginSucceed,
},
});
modals.openLogin(this, this.onLoginSucceed);
},
createPin() {
modals.openPinCreate(this, this.onPinCreated);
},
signUp() {
this.$buefy.modal.open({
parent: this,
component: SignUpForm,
hasModalCard: true,
events: {
'signup.succeed': this.onSignUpSucceed,
},
});
modals.openSignUp(this, this.onSignUpSucceed);
},
initializeUser(force = false) {
const self = this;


+ 12
- 0
pinry-spa/src/components/api.js 查看文件

@@ -3,6 +3,17 @@ import storage from './utils/storage';

const API_PREFIX = '/api/v2/';

const Pin = {
createFromURL(jsonData) {
const url = `${API_PREFIX}pins/`;
return axios.post(
url,
jsonData,
);
},
};


function fetchPins(offset, tagFilter, userFilter) {
const url = `${API_PREFIX}pins/`;
const queryArgs = {
@@ -160,6 +171,7 @@ const User = {
};

export default {
Pin,
fetchPin,
fetchPins,
fetchPinsForBoard,


+ 45
- 0
pinry-spa/src/components/modals.js 查看文件

@@ -0,0 +1,45 @@
import PinCreateModal from './pin_create/PinCreateModal.vue';
import LoginForm from './LoginForm.vue';
import SignUpForm from './SignUpForm.vue';


function openPinCreate(vm, onCreate) {
vm.$buefy.modal.open(
{
parent: vm,
component: PinCreateModal,
hasModalCard: true,
events: {
'create.succeed': onCreate,
},
},
);
}

function openLogin(vm, onSucceed) {
vm.$buefy.modal.open({
parent: vm,
component: LoginForm,
hasModalCard: true,
events: {
'login.succeed': onSucceed,
},
});
}

function openSignUp(vm, onSignUpSucceed) {
vm.$buefy.modal.open({
parent: vm,
component: SignUpForm,
hasModalCard: true,
events: {
'signup.succeed': onSignUpSucceed,
},
});
}

export default {
openPinCreate,
openLogin,
openSignUp,
};

+ 63
- 0
pinry-spa/src/components/pin_create/FileUpload.vue 查看文件

@@ -0,0 +1,63 @@
<template>
<div class="image-upload">
<div
v-show="previewExists"
class="has-text-centered is-center preview">
<img :src="previewImageURL">
</div>
<div v-show="!previewExists">
<b-field>
<b-upload v-model="dropFiles"
multiple
drag-drop>
<section class="section">
<div class="content has-text-centered">
<p>
<b-icon
icon="upload"
size="is-medium">
</b-icon>
</p>
<p>Drop your files here or click to upload</p>
</div>
</section>
</b-upload>
</b-field>
</div>
</div>
</template>

<script>
export default {
name: 'FileUpload',
props: {
previewImageURL: {
type: String,
default: null,
},
},
computed: {
previewExists() {
return this.previewImageURL !== null && this.previewImageURL !== '';
},
},
data() {
return {
dropFiles: [],
};
},
methods: {},
};
</script>

<style lang="scss" scoped>
@import '../utils/pin';
@import '../utils/loader';

.preview > img {
width: $pin-preview-width;
height: auto;
@include loader('../../assets/loader.gif');
}

</style>

+ 114
- 0
pinry-spa/src/components/pin_create/PinCreateModal.vue 查看文件

@@ -0,0 +1,114 @@
<template>
<div class="pin-create-modal">
<form action="">
<div class="modal-card" style="width: auto">
<header class="modal-card-head">
<p class="modal-card-title">New Pin</p>
</header>
<section class="modal-card-body">
<div class="columns">
<div class="column">
<FileUpload :previewImageURL="form.url.value"></FileUpload>
</div>
<div class="column">
<b-field label="Image URL"
:type="form.url.type"
:message="form.url.error">
<b-input
type="text"
v-model="form.url.value"
placeholder="where to fetch the image"
maxlength="256"
>
</b-input>
</b-field>
<b-field label="Image Referer"
:type="form.referer.type"
:message="form.referer.error">
<b-input
type="text"
v-model="form.referer.value"
placeholder="where to find the pin"
maxlength="256"
>
</b-input>
</b-field>
<b-field label="Descripton"
:type="form.description.type"
:message="form.description.error">
<b-input
type="textarea"
v-model="form.description.value"
placeholder="idea from this pin"
maxlength="1024"
>
</b-input>
</b-field>
<b-field label="Tags">
<b-taginput
v-model="form.tags.value"
ellipsis
icon="label"
placeholder="Add a tag">
</b-taginput>
</b-field>
</div>
</div>
</section>
<footer class="modal-card-foot">
<button class="button" type="button" @click="$parent.close()">Close</button>
<button
@click="createPin"
class="button is-primary">Create Pin
</button>
</footer>
</div>
</form>
</div>
</template>

<script>
import api from '../api';
import FileUpload from './FileUpload.vue';

function isURLBlank(url) {
return url !== null && url !== '';
}

export default {
name: 'PinCreateModal',
components: {
FileUpload,
},
data() {
return {
form: {
url: { value: null, error: null, type: null },
referer: { value: null, error: null, type: null },
description: { value: null, error: null, type: null },
tags: { value: [], error: null, type: null },
},
};
},
methods: {
createPin() {
const self = this;
if (isURLBlank(isURLBlank)) {
const data = {
url: this.form.url.value,
referer: this.form.referer.value,
description: this.form.description.value,
tags: this.form.tags.value,
};
const promise = api.Pin.createFromURL(data);
promise.then(
(resp) => {
self.$emit('pin.created', resp);
self.$parent.close();
},
);
}
},
},
};
</script>

+ 17
- 0
pinry-spa/src/views/PinCreate.vue 查看文件

@@ -0,0 +1,17 @@
<template>
<div class="pin-create">
<div></div>
</div>
</template>

<script>
// import PinCreateModal from './PinCreateModal.vue';

export default {
name: 'PinCreate',
};
</script>

<style scoped>

</style>

+ 11
- 0
pinry/middleware.py 查看文件

@@ -0,0 +1,11 @@
from django.middleware.csrf import get_token


class ForceCSRFCookieMiddleware:
def process_request(self, request):
if "CSRF_TOKEN" not in request.META:
get_token(request)
else:
if request.method != "GET":
get_token(request)
return

+ 3
- 0
pinry/settings/base.py 查看文件

@@ -26,12 +26,15 @@ INSTALLED_APPS = [
ROOT_URLCONF = 'pinry.urls'

MIDDLEWARE_CLASSES = [

'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'pinry.middleware.ForceCSRFCookieMiddleware',
'users.middleware.Public',
]



Loading…
取消
儲存