diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 51c92f4349..3c6f077364 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -18,7 +18,8 @@ Les contributions externes sont les bienvenues !
5. Assurez-vous que le code suit la [PEP-8](http://legacy.python.org/dev/peps/pep-0008/) : `flake8 --exclude=migrations,urls.py,settings.py --max-line-length=120 zds`
6. Si vous avez fait des modifications du _front_, jouez les tests associés : `gulp test`
7. Si vous modifiez le modèle (les fichiers models.py), n'oubliez pas de créer les fichiers de migration : `python manage.py schemamigration app_name --auto`
-8. Poussez votre travail et faites une _pull request_
+8. Si votre travail nécessite des actions spécifiques lors du déploiement, précisez-les dans le fichier [update.md](update.md).
+9. Poussez votre travail et faites une _pull request_
# Quelques bonnes pratiques
* Respectez [les conventions de code de Django](https://docs.djangoproject.com/en/1.6/internals/contributing/writing-code/coding-style/), ce qui inclut la [PEP 8 de Python](http://legacy.python.org/dev/peps/pep-0008/)
diff --git a/README.md b/README.md
index f567bd0b43..b114722fb5 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
[![Coverage Status](https://coveralls.io/repos/zestedesavoir/zds-site/badge.png?branch=dev)](https://coveralls.io/r/zestedesavoir/zds-site?branch=dev)
[![Licence GPL](http://img.shields.io/badge/license-GPL-yellow.svg)](http://www.gnu.org/licenses/quick-guide-gplv3.fr.html)
[![Documentation Status](https://readthedocs.org/projects/zds-site/badge/?version=latest)](https://readthedocs.org/projects/zds-site/?badge=latest)
+[![Requirements Status](https://requires.io/github/zestedesavoir/zds-site/requirements.svg?branch=dev)](https://requires.io/github/zestedesavoir/zds-site/requirements/?branch=dev)
+[![Dependency Status](https://david-dm.org/zestedesavoir/zds-site.svg)](https://david-dm.org/zestedesavoir/zds-site)
# Zeste de Savoir
diff --git a/assets/js/submit-dbclick.js b/assets/js/submit-dbclick.js
new file mode 100644
index 0000000000..f36d742dcd
--- /dev/null
+++ b/assets/js/submit-dbclick.js
@@ -0,0 +1,30 @@
+/* ===== Zeste de Savoir ====================================================
+ Prevent from double clic on submit buttons
+ ---------------------------------
+ Author: Alex-D / Alexandre Demode
+ ========================================================================== */
+
+(function($, undefined){
+ "use strict";
+
+ var buttonsSelector = "[type=submit], [type=reset]",
+ $lastButtonClicked = null;
+
+ $("body").on("submit", "form", function(){
+ $(buttonsSelector)
+ .addClass("disabled");
+
+ if($lastButtonClicked === null)
+ $lastButtonClicked = $(buttonsSelector, $(this));
+
+ $lastButtonClicked
+ .addClass("submitted")
+ .append($("", { class: "line-loading" }));
+ });
+ $("form").on("click", buttonsSelector, function(e){
+ $lastButtonClicked = $(this);
+
+ if($(this).hasClass("disabled"))
+ e.preventDefault();
+ });
+})(jQuery);
\ No newline at end of file
diff --git a/assets/scss/_all-supports.scss b/assets/scss/_all-supports.scss
index 60b6780d90..4c9900c221 100644
--- a/assets/scss/_all-supports.scss
+++ b/assets/scss/_all-supports.scss
@@ -61,13 +61,13 @@
color: #EEE;
padding: 4px 13px;
margin-left: 15px;
- background: $blue;
+ background: $primary;
text-decoration: none;
&:hover,
&:focus {
background: #EEE;
- color: $blue;
+ color: $primary;
}
}
@@ -89,15 +89,15 @@
#accept-cookies {
text-decoration: none;
background: #EEE;
- color: $blue;
+ color: $primary;
padding: 4px 15px;
border: none;
- transition: background .15s, color .15s;
+ transition: background $transition-duration, color $transition-duration;
margin-top: 3px;
&:hover,
&:focus {
- background: $blue;
+ background: $primary;
color: #EEE;
}
}
@@ -106,14 +106,14 @@
.header-container header {
background: #084561;
- border-bottom: 3px solid $orange;
+ border-bottom: 3px solid $secondary;
a,
button {
text-decoration: none;
color: #FFF;
transition-property: background;
- transition-duration: $default-transition-duration;
+ transition-duration: $transition-duration;
&:focus {
outline: none;
@@ -159,7 +159,7 @@
padding: 10px 2.5%;
font-size: 14px;
font-size: 1.4rem;
- border-bottom: 3px solid $orange;
+ border-bottom: 3px solid $secondary;
z-index: 50;
.dropdown-title {
@@ -195,7 +195,7 @@
height: 25px;
line-height: 25px;
color: #95d7f5;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
&:hover,
&:focus {
@@ -277,9 +277,9 @@
left: 0;
right: 0;
height: 2px;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
border-radius: 2px 2px 0 0;
- background-color: $orange;
+ background-color: $secondary;
}
&.active:before {
height: 0;
@@ -526,7 +526,7 @@
background: rgba(255, 255, 255, .25);
height: 40px;
transition-property: background;
- transition-duration: $default-transition-duration;
+ transition-duration: $transition-duration;
&:hover,
&:focus {
@@ -543,6 +543,12 @@
width: 12%;
text-indent: -9999px;
+ &.disabled {
+ opacity: .5;
+ background: transparent;
+ cursor: default;
+ }
+
&:after {
display: block;
content: " ";
@@ -570,7 +576,7 @@
font-size: 24px;
background: rgba(255, 255, 255, 1);
color: #084561;
- transition: background $default-transition-duration ease;
+ transition: background $transition-duration ease;
&:hover,
&:focus {
@@ -631,6 +637,14 @@
background: #e67e22;
}
+ &.ico-after {
+ padding-left: 40px;
+
+ &:after {
+ margin: 12px 0 0 13px;
+ }
+ }
+
a {
color: #EEE;
}
@@ -663,8 +677,8 @@
font-size: 16px;
font-size: 1.6rem;
position: relative;
- color: lighten($blue, 20%);
- transition: all $default-transition-duration ease;
+ color: lighten($primary, 20%);
+ transition: all $transition-duration ease;
&:first-child {
margin-top: 31px;
@@ -692,8 +706,8 @@
font-size: 1.8rem;
line-height: 38px;
line-height: 3.8rem;
- color: $blue;
- border-bottom: 1px solid $orange;
+ color: $primary;
+ border-bottom: 1px solid $secondary;
margin-top: 30px;
}
h4 {
@@ -738,7 +752,7 @@
li {
position: relative;
padding: 0 0 0 2.5%;
- transition: background $default-transition-duration ease;
+ transition: background $transition-duration ease;
&:not(.inactive):hover,
a:focus,
@@ -877,7 +891,7 @@
border: 1px solid #F0F0F0;
box-shadow: rgba(0, 0, 0, .1) 2px 2px 2px;
opacity: 0;
- transition: visibility 0s linear 0.15s, opacity 0.15s, left 0.15s;
+ transition: visibility 0s linear $transition-duration, opacity $transition-duration, left $transition-duration;
.avatar {
height: 40px;
@@ -909,7 +923,7 @@
visibility: visible;
left: 100%;
opacity: 1;
- transition: visibility 0s linear 0, opacity 0.15s, left 0.15s;
+ transition: visibility 0s linear 0, opacity $transition-duration, left $transition-duration;
}
}
}
@@ -926,7 +940,7 @@
a {
position: relative;
color: #084561;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
&:hover,
&:focus {
@@ -990,9 +1004,9 @@
font-size: 2.2rem;
line-height: 38px;
line-height: 3.8rem;
- color: $blue;
+ color: $primary;
font-weight: normal;
- border-bottom: 1px solid $orange;
+ border-bottom: 1px solid $secondary;
margin: 1px 0 15px;
&.illu {
@@ -1197,7 +1211,7 @@
input,
button {
- border: 1px solid $orange;
+ border: 1px solid $secondary;
background: #FFF;
margin: 5px 0 12px;
}
@@ -1213,7 +1227,7 @@
text-indent: -9999px;
border-left: none;
width: 40px;
- transition: background .15s;
+ transition: background $transition-duration;
position: absolute;
bottom: 0;
@@ -1337,20 +1351,16 @@
display: block;
text-decoration: none;
padding: 0 10px;
- background: #EEE;
- color: #777;
+ background: $header-hover;
+ color: #FFF;
margin-left: 1px;
- transition: all $default-transition-duration ease;
-
- &:before {
- content: "#";
- }
+ transition: all $transition-duration ease;
&:hover,
&:focus {
background: #FFF;
- color: lighten($blue, 15%);
- border-bottom: 1px solid lighten($blue, 15%);
+ color: $header-hover;
+ border-bottom: 1px solid $header-hover;
}
}
}
@@ -1402,11 +1412,11 @@
a {
display: block;
text-decoration: none;
- color: lighten($blue, 20%);
+ color: lighten($primary, 20%);
height: 36px;
line-height: 36px;
padding: 0 8px;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
&.ico-after {
padding-left: 30px;
@@ -1456,7 +1466,7 @@
min-width: 45px;
height: 40px;
line-height: 40px;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
&.current {
height: 38px;
@@ -1540,9 +1550,16 @@
margin-top: 50px !important;
margin-bottom: 50px !important;
+ h2 {
+ margin-bottom: 0 !important;
+ }
+ h2 + .topic {
+ border-top: none;
+ }
+
.topic {
position: relative;
- height: 81px;
+ min-height: 81px;
line-height: 25px;
border-top: 1px solid #FFF;
border-bottom: 1px solid #CCC;
@@ -1551,16 +1568,7 @@
clear: both;
&:first-child {
- overflow: visible;
-
- &:after {
- display: block;
- content: " ";
- width: 100%;
- height: 1px;
- background: #CCC;
- margin-top: -2px;
- }
+ border-top: 1px solid #CCC;
}
&:before {
@@ -1572,7 +1580,7 @@
width: 2px;
}
&.unread:before {
- background: lighten($blue, 20%);
+ background: lighten($primary, 20%);
}
&:nth-child(2n){
@@ -1590,21 +1598,21 @@
&.active {
&:before {
width: 5px;
- background: lighten($blue, 20%);
+ background: lighten($primary, 20%);
}
}
&.selected {
- background-color: lighten($blue, 75%);
+ background-color: lighten($primary, 75%);
}
}
a {
text-decoration: none;
- color: lighten($blue, 15%);
+ color: lighten($primary, 15%);
&:hover,
&:focus {
- color: lighten($blue, 15%);
+ color: lighten($primary, 15%);
text-decoration: underline;
outline: none;
}
@@ -1646,9 +1654,46 @@
margin: 5px 15px 0 0;
}
+ .topic-tags {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: inline;
+
+ .topic-tag {
+ display: block;
+ height: 23px;
+ line-height: 23px;
+ float: left;
+ padding: 0 5px;
+ margin: 0 3px 0 0;
+ color: $header-hover;
+ background: #FCFCFC;
+ border: 1px solid #CCC;
+
+ &:hover,
+ &:focus {
+ background: #FFF;
+ color: $primary;
+ border-color: $primary;
+ text-decoration: none;
+ }
+ &:focus {
+ box-shadow: $header-hover 0 0 3px;
+ }
+ }
+
+ li:last-child .topic-tag {
+ margin-right: 5px;
+ }
+ }
+
.topic-title-link {
+ display: block;
+ min-height: 48px;
+
&:hover,
- &:after {
+ &:focus {
text-decoration: none;
.topic-title {
@@ -1660,9 +1705,6 @@
.topic-title,
.topic-subtitle {
display: block;
- text-overflow: ellipsis;
- white-space: nowrap;
- overflow: hidden;
margin: 0;
padding: 0;
}
@@ -1672,29 +1714,26 @@
}
.topic-subtitle {
height: 24px;
- line-height: 1.3em;
+ line-height: 1.5em;
color: #777;
}
.topic-members {
margin: 0;
color: #777;
}
- .topic-tag:before {
- content: "#";
- }
}
.topic-answers {
- width: 13%;
+ width: 12%;
text-align: center;
padding-top: 29px;
}
.topic-last-answer {
- width: 22%;
+ width: 23%;
.topic-no-last-answer {
display: block;
- margin-top: 24px;
- color: $blue;
+ margin-top: 27px;
+ color: $primary;
opacity: .5;
}
}
@@ -1707,11 +1746,10 @@
.forum-list {
.group-title {
width: 100%;
- height: 50px;
margin-top: 30px !important;
clear: both;
border-bottom: 1px solid #CCC;
- color: $orange;
+ color: $secondary;
}
}
@@ -1751,8 +1789,11 @@
margin-top: 13px;
}
.forum-last-message {
- color: #777;
display: block;
+
+ .forum-last-message-long {
+ display: none;
+ }
}
.forum-last-message-title {
display: block;
@@ -1782,7 +1823,7 @@
> a,
p a,
ul:not(.pagination),
- ol {
+ ol:not(.summary-part) {
font-family: $font-serif-active;
}
}
@@ -1800,6 +1841,41 @@
}
}
+ .article-content .summary-part {
+ font-size: 20px;
+ color: darken($secondary, 11%);
+
+ h3,
+ h4 {
+ font-weight: normal;
+ width: 90%;
+
+ a {
+ text-decoration: none;
+
+ &:hover,
+ &:focus {
+ text-decoration: underline;
+ }
+ }
+ }
+ h3 {
+ font-size: 20px;
+ margin: 0 0 5px;
+ }
+
+ .summary-part {
+ list-style: none;
+ padding-left: 0;
+ margin-bottom: 15px;
+
+ h4 {
+ font-size: 14px;
+ margin: 2px 0;
+ }
+ }
+ }
+
.article-content,
.message-content {
margin-top: 20px;
@@ -1808,9 +1884,11 @@
h2,
h3 {
+ clear: both;
+
&,
a {
- color: #ee8709;
+ color: darken($secondary, 11%);
margin-top: 40px;
text-decoration: none;
}
@@ -2026,7 +2104,6 @@
// touche
kbd {
- $kbd-color: #F8F6EA;
background-color: $kbd-color;
padding: 2px 6px;
border-radius: 3px;
@@ -2061,8 +2138,8 @@
.comments-title {
margin: 50px 0 20px;
- color: $blue;
- border-bottom: 1px solid $orange;
+ color: $primary;
+ border-bottom: 1px solid $secondary;
font-weight: normal;
font-size: 22px;
font-size: 2.2rem;
@@ -2073,7 +2150,7 @@
.article-content {
p,
ul:not(.pagination),
- ol {
+ ol:not(.summary-part) {
font-family: $font-serif-active;
}
figcaption p {
@@ -2110,7 +2187,7 @@ table {
thead {
background: #DDD;
- color: $blue;
+ color: $primary;
}
th, td {
text-align: left;
@@ -2224,7 +2301,7 @@ table {
line-height: 26px;
width: 28px;
color: #777;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
&:first-child {
border-right: 0;
@@ -2300,12 +2377,12 @@ table {
line-height: 30px;
padding: 0 5px;
border-bottom: 1px solid #D2D5D6;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
&:hover,
&:focus {
- border-bottom: 1px solid lighten($blue, 15%);
- color: lighten($blue, 15%);
+ border-bottom: 1px solid lighten($primary, 15%);
+ color: lighten($primary, 15%);
outline: none;
}
}
@@ -2410,7 +2487,7 @@ table {
a {
color: #999;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
&:hover,
&:focus {
@@ -2428,7 +2505,7 @@ table {
button.ico-after {
border-bottom-width: 3px;
border-bottom-color: transparent;
- background: none;
+ background: none !important;
height: 32px;
&.upvote,
@@ -2438,6 +2515,17 @@ table {
width: 0;
}
}
+ &.disabled {
+ &,
+ &:hover,
+ &:focus {
+ border-bottom-color: transparent;
+
+ &:after {
+ opacity: .25 !important;
+ }
+ }
+ }
}
span.upvote,
span.downvote {
@@ -2452,12 +2540,12 @@ table {
text-overflow: ellipsis;
white-space: nowrap;
- &:hover,
- &:focus {
+ &:hover:not(.disabled),
+ &:focus:not(.disabled) {
color: #555;
- border-bottom-color: $upvote-green;
+ border-bottom-color: $upvote;
}
- &.active {
+ &.active:not(.disabled) {
color: #48a200;
&:after {
@@ -2472,12 +2560,12 @@ table {
}
}
.upvote {
- color: $upvote-green;
+ color: $upvote;
- &:hover,
- &:focus,
+ &:hover:not(.disabled),
+ &:focus:not(.disabled),
&.more-voted {
- border-bottom-color: $upvote-green;
+ border-bottom-color: $upvote;
}
&:not(.has-vote){
text-indent: -9999px;
@@ -2485,12 +2573,12 @@ table {
}
}
.downvote {
- color: $downvote-red;
+ color: $downvote;
- &:hover,
- &:focus,
+ &:hover:not(.disabled),
+ &:focus:not(.disabled),
&.more-voted {
- border-bottom-color: $downvote-red;
+ border-bottom-color: $downvote;
}
}
.voted:after {
@@ -2546,7 +2634,7 @@ table {
display: block;
float: left;
margin-left: 3px;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
&.ico-after {
padding-left: 30px !important;
@@ -2555,7 +2643,7 @@ table {
&:after {
top: 7px;
left: 7px;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
opacity: .5;
margin: 0;
}
@@ -2577,7 +2665,7 @@ table {
&:hover,
&:focus {
- border-bottom-color: lighten($blue, 15%);
+ border-bottom-color: lighten($primary, 15%);
outline: none;
background: none;
@@ -2618,7 +2706,7 @@ form.topic-message {
background: #777;
&.staff {
- background: $upvote-green;
+ background: $upvote;
}
}
@@ -2680,7 +2768,7 @@ form.topic-message {
list-style: none;
&.active, &:hover {
- background-color: lighten($blue, 10%);
+ background-color: lighten($primary, 10%);
color: white;
}
}
@@ -2709,14 +2797,14 @@ form.topic-message {
&:hover,
&.active {
- border-color: lighten($blue, 20%) !important;
+ border-color: lighten($primary, 20%) !important;
&:before {
display: none;
}
.topic-title {
- background: lighten($blue, 20%) !important;
+ background: lighten($primary, 20%) !important;
color: #FFF;
text-decoration: none;
}
@@ -2766,10 +2854,10 @@ form.topic-message {
}
&.selected {
- border-color: lighten($blue, 75%);
+ border-color: lighten($primary, 75%);
.topic-title {
- background: lighten($blue, 75%);
+ background: lighten($primary, 75%);
}
}
@@ -2784,7 +2872,7 @@ form.topic-message {
&:hover,
&:focus {
background-color: #CCC;
- color: lighten($blue, 20%);
+ color: lighten($primary, 20%);
}
}
}
@@ -2872,10 +2960,10 @@ form.topic-message {
padding: 20px 0;
}
.page-footer {
- background: darken($blue, 10%);
+ background: darken($primary, 10%);
height: 50px;
line-height: 50px;
- border-top: 3px solid $orange;
+ border-top: 3px solid $secondary;
font-size: 14px;
font-size: 1.4rem;
@@ -2904,7 +2992,7 @@ form.topic-message {
&:hover,
&:focus {
- border-bottom-color: $orange;
+ border-bottom-color: $secondary;
}
}
@@ -2942,12 +3030,12 @@ form.topic-message {
.modal-title {
display: block;
- border-bottom: 3px solid $orange;
+ border-bottom: 3px solid $secondary;
line-height: 53px;
height: 50px;
text-indent: 15px;
margin-bottom: 20px;
- background: $blue;
+ background: $primary;
color: #FFF;
font-size: 16px;
font-size: 1.6rem;
@@ -2997,7 +3085,7 @@ form.topic-message {
.btn-submit:not(.modal-inner),
[type=submit]:not(.modal-inner) {
height: 51px;
- color: $blue;
+ color: $primary;
font-weight: bold;
}
.btn-cancel {
diff --git a/assets/scss/_base.scss b/assets/scss/_base.scss
index e4ac75fc03..9fba00735b 100644
--- a/assets/scss/_base.scss
+++ b/assets/scss/_base.scss
@@ -70,11 +70,11 @@ textarea {
}
a {
- color: lighten($blue, 20%);
- transition: all $default-transition-duration ease;
+ color: lighten($primary, 20%);
+ transition: all $transition-duration ease;
&:hover {
- color: darken($orange, 15%);
+ color: darken($secondary, 15%);
text-decoration: none;
}
}
diff --git a/assets/scss/_editor.scss b/assets/scss/_editor.scss
index ccc3887e03..8be5ce8fdd 100644
--- a/assets/scss/_editor.scss
+++ b/assets/scss/_editor.scss
@@ -65,7 +65,7 @@
button {
&:hover,
&:focus {
- border-bottom-color: lighten($blue, 20%);
+ border-bottom-color: lighten($primary, 20%);
outline: none;
background-color: #EEE;
}
@@ -274,7 +274,7 @@ div.zform-popup {
}
.btn-submit,
[type=submit] {
- color: $blue;
+ color: $primary;
font-weight: bold;
}
.btn-cancel {
diff --git a/assets/scss/_form.scss b/assets/scss/_form.scss
index d8f1a9605a..29b8bb8239 100644
--- a/assets/scss/_form.scss
+++ b/assets/scss/_form.scss
@@ -71,7 +71,8 @@
}
}
- &[disabled] {
+ &[disabled],
+ .disabled {
background: #DDD !important;
color: #555;
}
@@ -81,7 +82,7 @@
button,
.btn {
-webkit-appearance: none;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
}
input:not([type=submit]):not([type=reset]):not([type=radio]):not([type=checkbox]) {
@@ -111,6 +112,7 @@
[type=submit],
button,
.btn {
+ position: relative;
height: 40px;
line-height: 40px;
cursor: pointer;
@@ -121,23 +123,59 @@
text-decoration: none;
margin-left: 1px;
outline: none;
+
+ &.submitted {
+ color: #555;
+
+ // Animation loading on submit buttons
+ .line-loading {
+ display: block;
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ width: 0%;
+ height: 1px;
+ background: #555;
+ animation: lineLoading linear 1s infinite;
+ }
+ }
}
[type=submit],
.btn-submit {
color: #FFF;
- background: $upvote-green;
+ background: $upvote;
&:not([disabled]):hover,
- &:not([disabled]):focus {
- background: lighten($upvote-green, 5%);
+ &:not([disabled]):focus,
+ &:not(.disabled):hover,
+ &:not(.disabled):focus {
+ background: lighten($upvote, 5%);
+ }
+
+ &.disabled.submitted {
+ color: $upvote;
+
+ .line-loading {
+ background: $upvote;
+ }
}
}
.btn-cancel {
- background: $downvote-red;
+ background: $downvote;
&:not([disabled]):hover,
- &:not([disabled]):focus {
- background: lighten($downvote-red, 7%);
+ &:not([disabled]):focus,
+ &:not(.disabled):hover,
+ &:not(.disabled):focus {
+ background: lighten($downvote, 7%);
+ }
+
+ &.disabled.submitted {
+ color: $downvote;
+
+ .line-loading {
+ background: $downvote;
+ }
}
}
.btn-grey {
@@ -145,15 +183,29 @@
color: #555;
&:not([disabled]):hover,
- &:not([disabled]):focus {
+ &:not([disabled]):focus,
+ &:not(.disabled):hover,
+ &:not(.disabled):focus {
background: #CCC;
color: #333;
}
+
+ &.disabled.submitted {
+ color: #555;
+
+ .line-loading {
+ background: #999;
+ }
+ }
}
- [disabled] {
- cursor: default;
- background: #F7F7F7;
- color: #CCC;
+ [disabled],
+ .disabled {
+ cursor: default !important;
+ background: #EEE !important;
+
+ &:not(.submitted){
+ color: #BBB !important;
+ }
}
.form-sub-link {
@@ -164,6 +216,7 @@
.buttonHolder {
margin-top: 25px;
+ min-height: 40px;
}
}
@@ -212,4 +265,28 @@
padding-top: 3px;
padding-bottom: 0;
}
+}
+
+
+
+
+@keyframes lineLoading {
+ 0% {
+ width: 0%;
+ left: 0;
+ right: inherit;
+ }
+ 49% {
+ left: 0;
+ right: inherit;
+ }
+ 50% {
+ width: 100%;
+ left: inherit;
+ right: 0;
+ }
+ 100% {
+ left: inherit;
+ right: 0;
+ }
}
\ No newline at end of file
diff --git a/assets/scss/_mobile-tablet.scss b/assets/scss/_mobile-tablet.scss
index 0481e2b5ba..d99fa1c65f 100644
--- a/assets/scss/_mobile-tablet.scss
+++ b/assets/scss/_mobile-tablet.scss
@@ -14,7 +14,7 @@
.page-container,
.mobile-menu {
transition-property: transform;
- transition-duration: .3s;
+ transition-duration: $transition-duration * 2;
transition-timing-function: ease;
}
}
@@ -469,6 +469,40 @@
.topic-list {
.topic {
background: none !important;
+
+ p {
+ margin: 0 !important;
+ }
+
+ .topic-members {
+ .topic-members-long-date {
+ display: none;
+ }
+ }
+ .topic-answers {
+ padding: 0;
+ width: 30%;
+ border-bottom: 1px solid #CCC;
+
+ &.topic-no-answer {
+ display: none;
+ }
+ }
+ .topic-last-answer {
+ width: 30%;
+ padding: 0;
+ text-align: right;
+
+ .topic-last-answer-short-date {
+ font-size: 1.3rem;
+ }
+ .topic-last-answer-long-date {
+ display: none;
+ }
+ .topic-no-last-answer {
+ text-align: center;
+ }
+ }
}
}
@@ -603,15 +637,18 @@
.full-content-wrapper {
h1,
h2,
- h3 {
+ h3,
+ .subtitle {
padding-left: 10px;
padding-right: 10px;
}
+ .illu img {
+ display: none;
+ }
h4,
h5,
h6,
- .subtitle,
.authors,
p,
figure,
@@ -627,6 +664,13 @@
margin-right: 0;
}
}
+
+ .license {
+ position: absolute;
+ margin-top: 0;
+ top: 62px;
+ right: 15px;
+ }
}
}
@@ -643,7 +687,7 @@
height: auto;
p {
- border-bottom: 1px solid darken($orange, 40%);
+ border-bottom: 1px solid darken($secondary, 40%);
}
p,
ul {
diff --git a/assets/scss/_mobile.scss b/assets/scss/_mobile.scss
index 4fe69521e9..6802cf4532 100644
--- a/assets/scss/_mobile.scss
+++ b/assets/scss/_mobile.scss
@@ -76,6 +76,29 @@
padding-top: 0 !important;
}
+ .topic-list {
+ .topic-infos .ico-after:after {
+ margin: 4px 0 0 2px;
+ }
+ .topic-description {
+ .topic-subtitle:empty {
+ display: none;
+ }
+ }
+ .topic-last-answer {
+ .topic-no-last-answer {
+ font-size: 1.3rem;
+ }
+ }
+ }
+ .forum-list {
+ .topic-description {
+ .topic-subtitle {
+ margin-left: 10px;
+ }
+ }
+ }
+
.topic-message .message {
.message-actions a {
width: 0px;
diff --git a/assets/scss/_tablet.scss b/assets/scss/_tablet.scss
index 0a7e98ebf8..2553641597 100644
--- a/assets/scss/_tablet.scss
+++ b/assets/scss/_tablet.scss
@@ -32,7 +32,7 @@
&::-webkit-scrollbar-thumb {
background-color: #396a81;
border: 1px solid #06354a;
- transition: all $default-transition-duration ease;
+ transition: all $transition-duration ease;
&:hover {
background-color: #5196b6;
diff --git a/assets/scss/_wide.scss b/assets/scss/_wide.scss
index 677ba91b47..e282234d24 100644
--- a/assets/scss/_wide.scss
+++ b/assets/scss/_wide.scss
@@ -278,6 +278,15 @@
margin: 0;
}
}
+
+ .article-content > .summary-part > li {
+ float: left;
+ width: 50%;
+
+ &:nth-child(2n+1) {
+ clear: both;
+ }
+ }
}
.sidebar {
@@ -394,6 +403,37 @@
+ /* ============
+ Main / Topic list
+ ============ */
+ .topic-list {
+ .topic-members-short-date {
+ display: none;
+ }
+ .topic-last-answer-short-date {
+ display: none;
+ }
+ &:not(.topic-list-small) .topic-last-answer {
+ .topic-no-last-answer {
+ margin-top: 24px;
+ }
+ }
+ }
+ .forum-list {
+ .topic {
+ min-height: 0;
+ }
+ .topic-last-answer {
+ .forum-last-message {
+ .forum-last-message-long {
+ display: none;
+ }
+ }
+ }
+ }
+
+
+
/* ============
Main / Message
============ */
@@ -526,7 +566,7 @@
&:hover,
&:focus {
color: #EEE !important;
- background: $blue !important;
+ background: $primary !important;
}
}
}
diff --git a/assets/scss/main.scss b/assets/scss/main.scss
index 14c7ada2e7..5544ef5091 100644
--- a/assets/scss/main.scss
+++ b/assets/scss/main.scss
@@ -11,9 +11,9 @@
/**
* Global vars
*/
-$default-transition-duration: .15s;
+$transition-duration: .15s;
-//$sprite: sprite-map("../images/sprite.png");
+//$sprite: url("../images/sprite.png");
$sprite2x: url("../images/sprite@2x.png");
$sprite: $sprite2x;
@@ -22,20 +22,23 @@ $sprite: $sprite2x;
/**
* Colors
*/
-$body-background: #F7F7F7;
-$blue: #084561;
-$orange: #F8AD32;
-$header-hover: #396a81;
-$helpful: #e9f9dc;
+$primary: #084561; // Blue for ZdS
+$secondary: #F8AD32; // Orange for ZdS
-$sidebar-background: #F0F0F0;
-$sidebar-hover: #FFF;
+$body-background: #F7F7F7; // Background of page body
+$header-hover: #396a81; // Hover background of links in header
+$helpful: #e9f9dc; // Background of helpful messages
-$upvote-green: #48a200;
-$downvote-red: #c0392b;
+$sidebar-background: #F0F0F0; // Background of sidebar
+$sidebar-hover: #FFF; // Background of links on hover
+
+$upvote: #48a200; // Green for ZdS / Upvote text & staff badge & submit button
+$downvote: #c0392b; // Red for ZdS / Downvote text & cancel button
+
+$kbd-color: #F8F6EA; // Base color for keyboard tag
// Margins
-$modal-margin: 25px;
+$modal-margin: 25px; // Default modal margin (biggest size)
// Font Famillies
$font-sans-serif: "Segoe UI", "Trebuchet MS", Helvetica, "Helvetica Neue", Arial, sans-serif;
diff --git a/doc/sphinx/source/gallery/gallery.rst b/doc/sphinx/source/gallery/gallery.rst
index 0f737db92d..9bc016d86f 100644
--- a/doc/sphinx/source/gallery/gallery.rst
+++ b/doc/sphinx/source/gallery/gallery.rst
@@ -2,27 +2,27 @@
Les galeries
============
-La liste des galleries de l'utilisateur est accessible via l'url suivante : ``/galerie/``.
+La liste des galeries de l'utilisateur est accessible via l'url suivante : ``/galerie/``.
Généralités
===========
-Une galerie rassemble physiquement un certain nombre d'image. Elle peut prendre un titre ainsi qu'un sous-titre.
+Une galerie rassemble physiquement un certain nombre d'images. Elle peut prendre un titre ainsi qu'un sous-titre.
Création et remplissage
-----------------------
-On peut créer une nouvelle galerie via l'url ``/galerie/nouveau/``, ou il est nécéssaire de renseigner ces deux champs.
+On peut créer une nouvelle galerie via l'url ``/galerie/nouveau/``, où il est nécessaire de renseigner ces deux champs.
.. attention::
- Des galeries sont créée **automatiquement** à la création d'un nouveau `tutoriel <../tutorial/tutorial.html>`_ par un utilisateur et possède alors le même nom que celui-ci.
+ Des galeries sont créées **automatiquement** à la création d'un nouveau `tutoriel <../tutorial/tutorial.html>`_ par un utilisateur et possède alors le même nom que celui-ci.
Il est ensuite possible d'uploader des images via le menu de gauche :
.. figure:: images/menu-gauche.png
:align: center
- Liens permetant d'uploader des images
+ Liens permettant d'uploader des images
Via celui-ci, on peut importer des archives contenant des images (au format ZIP) ou des images seules. Dans ce dernier cas, le formulaire d'*upload* est le suivant :
@@ -31,7 +31,7 @@ Via celui-ci, on peut importer des archives contenant des images (au format ZIP)
Formulaire d'upload de nouvelles images
-Comme on peut le voir, chaque image doit posséder au minimum un titre et peut posséder une légende, qui sera employée par la suite. Il est donc conseillé de remplir également ce second champ, bien que ce ne soit pas obligatoire. Quand à l'image elle-même, sa taille ne peut pas excéder 1024 Kio.
+Comme on peut le voir, chaque image doit posséder au minimum un titre et peut posséder une légende, qui sera employée par la suite. Il est donc conseillé de remplir également ce second champ, bien que ce ne soit pas obligatoire. Quant à l'image elle-même, sa taille ne peut pas excéder 1024 Kio.
Une fois l'image uploadée, il est possible d'effectuer différentes actions sur celle-ci sur la page spécifique à celle-ci :
@@ -42,13 +42,16 @@ Une fois l'image uploadée, il est possible d'effectuer différentes actions sur
Autrement dit,
-+ En modifier le titre, la légende ou encore l'image en elle-même. À noter que le titre et la légende peuvent être modifiés **sans qu'il ne soit nécéssaire** d'uploader une nouvelle image.
++ En modifier le titre, la légende ou encore l'image en elle-même. À noter que le titre et la légende peuvent être modifiés **sans qu'il ne soit nécessaire** d'uploader une nouvelle image.
+ Obtenir le code à insérer dans un champ de texte acceptant le markdown pour l'image en elle-même, sa miniature ou encore la miniature accompagnée du lien vers l'image en taille réelle.
+.. attention::
+ Le titre de l'image n'entre pas en compte dans le nommage de l'image une fois cette dernière téléchargée. Afin de s'assurer l'unicité des noms, nous utilisons un algorithme de hashage pour cela.
+
Les utilisateurs et leurs droits
--------------------------------
-Le créateur de la gallerie possède un droit d'écriture, mais peut rajouter à tout moment des utilisateurs dans celle-ci :
+Le créateur de la galerie possède un droit d'écriture, mais peut rajouter à tout moment des utilisateurs dans celle-ci :
.. figure:: images/gestion-auteurs.png
:align: center
@@ -62,18 +65,19 @@ Lors d'un clic sur "Ajouter un utilisateur", une fenêtre modale s'ouvre :
Choix de l'utilisateur et sélection de ces droits
-Il est alors possible de rajouter un nouvel utilisateur dans la gallerie. Les droits de celui-ci peuvent-être les suivants :
+Il est alors possible de rajouter un nouvel utilisateur dans la galerie. Les droits de celui-ci peuvent-être les suivants :
+
++ **Lecture** : (``zds.gallery.models.GALLERY_READ``) l'utilisateur a seulement le droit de consulter les images existantes dans la galerie sans pouvoir apporter de modifications;
++ **Écriture** : (``zds.gallery.models.GALLERY_WRITE``), inclut **Lecture**, l'utilisateur peut modifier ou supprimer des images existantes, en rajouter des nouvelles et changer les attributs de la galerie (y compris ajouter de nouveaux utilisateurs);
-+ **Écriture** : l'utilisateur peut modifier ou supprimer des images existantes, en rajouter des nouvelles et changer les atributs de la gallerie (y compris ajouter de nouveaux utilisateurs)
-+ **Lecture** : l'utilisateur a seulement le droit de consulter les images existantes dans la gallerie sans pouvoir apporter de modifications.
-Il n'est actuelement pas possible de modifier les droits d'un utilisateur après son ajout à la galerie.
+Il n'est actuellement pas possible de modifier les droits d'un utilisateur après son ajout à la galerie.
La suppression
--------------
-Une image peut être supprimée à tout moment en la sélectionnant sur la page de la gallerie et en cliquant sur le bouton suivant :
+Une image peut être supprimée à tout moment en la sélectionnant sur la page de la galerie et en cliquant sur le bouton suivant :
.. figure:: images/sup-image.png
:align: center
@@ -82,7 +86,7 @@ Une image peut être supprimée à tout moment en la sélectionnant sur la page
Attention qu'aucune confirmation n'est demandée pour la suppression d'une image.
-Une galerie peut être quand a elle supprimée via la page de gestion des galeries (``/galerie/``) en cochant la case de celle-ci et en cliquant sur "supprimer les galleries sélectionnées" dans le menu de gauche :
+Une galerie peut être quant à elle supprimée via la page de gestion des galeries (``/galerie/``) en cochant la case de celle-ci et en cliquant sur "supprimer les galeries sélectionnées" dans le menu de gauche :
.. figure:: images/sup-galerie.png
:align: center
@@ -96,16 +100,32 @@ Une modale s'ouvre ensuite, demandant de confirmer le choix :
Confirmation
-Une fois cliqué sur "confirmer", la gallerie et les images qu'elle contient sont supprimées.
+Une fois cliqué sur "confirmer", la galerie et les images qu'elle contient sont supprimées.
.. attention::
Si une galerie est liée à un tutoriel existant, elle ne peut pas être supprimée.
+Lien galerie <-> Tutoriel
+=========================
+
+Chaque tutoriel possède une galerie en propre. Par défaut cette galerie possède le même nom qui a été donné au tutoriel lors de sa création.
+
+Chaque auteur possède un droit d'accès en écriture (``GALLERY_WRITE``) sur la galerie liée au tutoriel.
+
+Si un membre possède un droit de lecture seule (``GALLERY_READ``) sur la galerie d'un tutoriel, aucun droit n'est accordé à ce membre quant au tutoriel.
+
+À l'heure actuelle, les articles ne possèdent pas de galerie.
+
Aspects techniques
==================
-Chaque galerie (classe ``Gallery``) est stockée en base de donnée avec son titre, son sous-titre et son *slug* (ainsi que la date de création et de dernière modification). Une galerie est ratachée à l'utilisateur via la classe ``UserGallery``, qui reprend un lien vers l'utilisateur, la gallerie, mais également les droits qu'il possède sur cette dernière, sous la forme d'une lettre unique : ``R`` pour le droit de lecture ou ``W`` pour le droit d'écriture.
+Chaque galerie (classe ``Gallery``) est stockée en base de données avec son titre, son sous-titre et son *slug* (ainsi que la date de création et de dernière modification). Une galerie est rattachée à l'utilisateur via la classe ``UserGallery``, qui reprend un lien vers l'utilisateur, la galerie, mais également les droits qu'il possède sur cette dernière, sous la forme d'une constante : ``GALLERY_READ`` pour le droit de lecture ou ``GALLERY_WRITE`` pour le droit d'écriture.
+
+Une image (classe ``Image``) est renseignée en base de données avec son titre, sa légende, un lien vers la galerie qui la contient, son *slug* et un lien *physique* vers le fichier image (ainsi que la date de création et de dernière modification).
+
+Les images sont stockées dans le dossier renseigné par la variable ``MEDIA_URL`` (dans le fichier ``settings.py``), dans un sous-dossier dont le nom correspond au ``pk`` de la galerie. C'est la librairie `easy_thumbnails `_ qui gère la génération des miniatures correspondantes aux images uploadées, à la demande du *back*.
-Une image (classe ``Image``) est renseignée en base de donnée avec son titre, sa légende, un lien vers la galerie qui la contient, son *slug* et un lien *physique* vers le fichier image (ainsi que la date de création et de dernière modification).
+Outils logiciels utilisés
+=========================
-Les images sont stockée dans le dossier renseigné par la variable ``MEDIA_URL`` (dans le fichier ``settings.py``), dans un sous-dossier dont le nom correspond au ``pk`` de la galerie. C'est la libraire `easy_thumbnails `_ qui gère la génération des miniatures correspondantes aux images uploadées, à la demande du *back*.
\ No newline at end of file
+Afin d'assurer une compatibilité maximale de toutes les images des galeries, leur redimensionnement au besoin... le logiciel pyllow est utilisé.
diff --git a/doc/sphinx/source/install/install-linux.rst b/doc/sphinx/source/install/install-linux.rst
index dc0f5f745c..f308735dbd 100644
--- a/doc/sphinx/source/install/install-linux.rst
+++ b/doc/sphinx/source/install/install-linux.rst
@@ -86,3 +86,37 @@ Pour faire fonctionner ZdS dans son ensemble (ceci n'est pas obligatoire) vous p
cabal update
cabal install pandoc
+Ajouter un hook de pre-commit a git pour tester flake
+-----------------------------------------------------
+
+Afin de s'assurer qu'aucune erreur de mise en forme ne passe les commits,
+il peut être utile de rajouter un hook de pre-commit à git. Un hook est un petit
+programme qui sera exécuté avant une action particulière de git. En l'occurence nous
+allons rajouter un hook qui s'executera juste avant la validation d'un commit.
+
+Pour cela, commencer par créer et éditer le fichier `.git/hooks/pre-commit`
+
+Ensuite, il ne reste plus qu'à rajouter le contenu suivant dans ce fichier et dorénavant
+le controle flake (pour le respect PEP) sera exécuté avant la validation du message de commit.
+Ainsi, plus aucune erreur flake ne viendra vous embêter à posteriori et la base de code
+restera propre et lisible au cours du temps !
+
+.. sourcecode:: bash
+
+ #!/bin/sh
+
+ flake8 --exclude=migrations,urls.py,settings.py --max-line-length=120 zds
+
+ # Store tests result
+ RESULT=$?
+
+ [ $RESULT -ne 0 ] && exit 1
+ exit 0
+
+
+Enfin n'oubliez pas de le rendre executable via chmod
+
+.. sourcecode:: bash
+
+ chmod +x .git/hooks/pre-commit
+
diff --git a/doc/sphinx/source/introduction.rst b/doc/sphinx/source/introduction.rst
index 0c635e8d1f..28c3d870df 100644
--- a/doc/sphinx/source/introduction.rst
+++ b/doc/sphinx/source/introduction.rst
@@ -16,3 +16,64 @@ Si vous voulez installer et démarrer une instance locale de ZdS, vous devez cli
install/install-windows
install/install-os-x
install/install-linux
+
+Il est possible de personnaliser ZdS pour n'importe quel site communautaire de partage. Un ensemble de paramètres est disponible dans le fichier ``settings.py`` via un dictionnaire. Vous pourrez donc écraser ces variables par défaut dans votre fichier ``settings_prod.py``. Le dictionnaire de variables relatives au site est donc le suivant :
+
+.. sourcecode:: python
+
+ ZDS_APP = {
+ 'site': {
+ 'name': u"ZesteDeSavoir",
+ 'litteral_name': u"Zeste de Savoir",
+ 'slogan': u"Zeste de Savoir, la connaissance pour tous et sans pépins",
+ 'abbr': u"zds",
+ 'url': u"http://127.0.0.1:8000",
+ 'dns': u"zestedesavoir.com",
+ 'email_contact': u"communication@zestedesavoir.com",
+ 'email_noreply': u"noreply@zestedesavoir.com",
+ 'repository': u"https://github.com/zestedesavoir/zds-site",
+ 'short_description': u"",
+ 'long_description': u"Zeste de Savoir est un site de partage de connaissances "
+ u"sur lequel vous trouverez des tutoriels de tous niveaux, "
+ u"des articles et des forums d'entraide animés par et pour "
+ u"la communauté.",
+ 'year': u"2014",
+ 'association': {
+ 'name': u"Zeste de Savoir",
+ 'fee': u"30 €",
+ 'email': u"association@zestedesavoir.com",
+ 'email_ca': u"ca-zeste-de-savoir@googlegroups.com"
+ },
+ 'hosting': {
+ 'name': u"OVH",
+ 'address': u"2 rue Kellermann - 59100 Roubaix - France"
+ },
+ 'cnil': u"1771020",
+ },
+ 'member': {
+ 'bot_account': u"admin",
+ 'members_per_page': 100,
+ },
+ 'gallery': {
+ 'image_max_size': 1024 * 1024,
+ },
+ 'article': {
+ 'repo_path': os.path.join(SITE_ROOT, 'articles-data'),
+ },
+ 'tutorial': {
+ 'repo_path': os.path.join(SITE_ROOT, 'tutoriels-private'),
+ 'repo_public_path': os.path.join(SITE_ROOT, 'tutoriels-public'),
+ 'default_license_pk': 7
+ },
+ 'forum': {
+ 'posts_per_page': 21,
+ 'topics_per_page': 21,
+ 'spam_limit_seconds': 60 * 15,
+ 'spam_limit_participant': 2,
+ 'followed_topics_per_page': 21,
+ 'beta_forum_id': 1,
+ 'max_post_length': 1000000,
+ 'top_tag_max': 5,
+ }
+ }
+
diff --git a/doc/sphinx/source/member/member.rst b/doc/sphinx/source/member/member.rst
index 8cfaf8838a..0a457c17a0 100644
--- a/doc/sphinx/source/member/member.rst
+++ b/doc/sphinx/source/member/member.rst
@@ -55,11 +55,11 @@ Le clic sur "me désinscrire" entraîne alors une série d'action (qui sont **ir
- pour les sujets du forum (qui, cependant, restent ouverts)
- pour les messages des MP (le membre quitte les discussions auxquelles il participait) ;
- pour les commentaires aux tutoriels et articles ;
- - les `galeries`_ non liées à un tutoriel sont données à ``Auteur externe`` (puisque l’image peut être considérée comme venant d’un “auteur”) avec droit de lecture et d’écriture ;
+ - les `galeries`_ non liées à un tutoriel sont données à ``external`` (puisque l’image peut être considérée comme venant d’un “auteur”) avec droit de lecture et d’écriture ;
- les `articles`_ et `tutoriels`_ suivent ces règles :
- si le tutoriel/article a été écrit par plusieurs personnes : le membre est retiré de la liste des auteurs ;
- - si le tutoriel/article est *publié*, il passe sur le compte “Auteur externe”. Une demande expresse sera nécessaire au retrait complet de ces contenus ;
+ - si le tutoriel/article est *publié*, il passe sur le compte “external”. Une demande expresse sera nécessaire au retrait complet de ces contenus ;
- si le tutoriel/article n’est pas publié (brouillon, bêta, validation) il est supprimé, ainsi que la galerie qui lui est associée.
.. _galeries: ../gallery/gallery.html
@@ -76,7 +76,7 @@ Afin de faciliter les procédures de tests en local, 6 utilisateurs ont été cr
- staff/staff : Utilisateur avec les droits d'un staff
- admin/admin : Utilisateur avec les droits d'un staff et d'un admin
- anonymous/anonymous : Utilisateur qui permet l'anonymisation des messages sur les forums, dans les commentaires d'articles et de tutoriels ainsi que dans les MPs
-- Auteur externe/external : Utilisateur qui permet de récupérer les tutoriels d'anciens membres et/ou de publier des tutoriels externes.
+- external/external : Utilisateur qui permet de récupérer les tutoriels d'anciens membres et/ou de publier des tutoriels externes.
- ïtrema/ïtrema : Utilisateur de test supplémentaire sans droit
Pour que ces membres soient ajoutés à la base de données, il est donc nécéssaire d'exécuter la commande, suivante, à la racine du site
@@ -87,18 +87,18 @@ Pour que ces membres soient ajoutés à la base de données, il est donc nécés
.. attention::
- Les utilisateurs ``anonymous`` et ``Auteur externe`` **doivent** être présents dans la base de données pour le bon fonctionnement du site.
+ Les utilisateurs ``anonymous`` et ``external`` **doivent** être présents dans la base de données pour le bon fonctionnement du site.
En effet, ils permettent le bon fonctionnement du processus d'anonymisation (voir `plus haut <#desinscription>`_)
-Les utilisateurs ``anonymous`` et ``Auteur externe`` sont totalement paramétrables dans le fichier ``zds/settings.py`` :
-pour changer le nom d'utilisateur (*username*) de ces comptes, agissez sur les constantes suivantes :
+Les utilisateurs ``anonymous`` et ``external`` sont totalement paramétrables dans le fichier ``zds/settings.py`` :
+pour changer le nom d'utilisateur (*username*) de ces comptes, agissez sur les constantes suivantes (du dictionnaire ZDS_APP):
.. sourcecode:: python
# Constant for anonymisation
- ANONYMOUS_USER = "anonymous"
- EXTERNAL_USER = "Auteur externe"
+ anonymous_account = "anonymous"
+ external_account = "external"
Bien entendu, les comptes correspondants doivent exister dans la base de donnée.
diff --git a/doc/sphinx/source/tutorial/tutorial.rst b/doc/sphinx/source/tutorial/tutorial.rst
index 776afcf096..b7476f2f4b 100644
--- a/doc/sphinx/source/tutorial/tutorial.rst
+++ b/doc/sphinx/source/tutorial/tutorial.rst
@@ -5,44 +5,46 @@ Les tutoriels
Les composantes d'un tutoriel
=============================
-Un tutoriel n'est qu'un ensemble d'introduction de corps et de conclusion.
+Un tutoriel n'est qu'un ensemble d'introduction, de corps et de conclusion.
Cependant, selon la taille souhaitée du tutoriel, il peut nécessiter une organisation
-particulière. C'est pourquoi les tutoriel de ZdS sont structurés en **quatre** niveaux.
+particulière. C'est pourquoi les tutoriels de ZdS sont structurés en **quatre** niveaux.
Niveau 1 : Le tutoriel
----------------------
-Ce niveau définit l'ensemble des métadonées assocciées à un tutoriel, c'est le plus haut
-niveau de touts les tutoriels. on y retrouve les informations suivantes :
+Ce niveau définit l'ensemble des métadonnées associées à un tutoriel, c'est le plus haut
+niveau de tous les tutoriels. On y retrouve les informations suivantes :
- titre
- description
- logo
- licence
- sous-catégories
-- introducton
+- introduction
- conclusion
Niveau 2 : La partie
--------------------
Ce niveau adapté à des documents un peu plus longs, permet de subdiviser notre contenu
-en plusieurs parties. C'est typiquement le niveau qui sera utile si vous souhaiter rédiger
+en plusieurs parties. C'est typiquement le niveau qui sera utile si vous souhaitez rédiger
un mémoire, ou une thèse. Par contre, pour un simple article, ce n'est pas la peine d'en faire
des parties. Un tutoriel peut contenir plusieurs parties et une partie comprend essentiellement
les informations suivantes :
+
- titre
- introduction
- conclusion
- position (dans le tutoriel)
-Niveau 3 : La chapitre
+Niveau 3 : Le chapitre
----------------------
-On retrouve la encore les mêmes notions qu'on peut rencontrer dans la rédaction d'un document
+On retrouve là encore les mêmes notions qu'on peut rencontrer dans la rédaction d'un document
pédagogique. Si une partie peut contenir plusieurs chapitres (on le verra plus loin dans le cas
-des bigtutos), un chapitre peut être rattaché a un tutoriel directement (minituto) .
+des bigtutos), un chapitre peut être rattaché à un tutoriel directement (minituto).
Le chapitre comprend les informations suivantes :
+
- titre
- introduction
- conclusion
@@ -50,22 +52,22 @@ Le chapitre comprend les informations suivantes :
Niveau 4 : L'extrait
--------------------
-L'extrait est la niveau le plus fin de subdivision d'un tutoriel. Il constitue le contenu, ou encore
+L'extrait est le niveau le plus fin de subdivision d'un tutoriel. Il constitue le contenu, ou encore
le corps de notre texte. Un extrait est constitué uniquement d'un bloc de texte.
Les types de tutoriels
======================
-Il existe actuellement deux types de tutoriels, les minituto et les bigtutos,
-Même s'il y'a une réflexion sur la création de moyen-tuto, laissez moi déjà
-vous présenter ce qui existe aujourdhui.
+Il existe actuellement deux types de tutoriels, les minitutos et les bigtutos,
+Même s'il y a une réflexion sur la création de moyen-tutos, laissez moi déjà
+vous présenter ce qui existe aujourd'hui.
Les minitutos
-------------
Un minituto est un format de tutoriel pour du contenu léger. Fonctionnellement il s'agit d'un tutoriel
-qui ne contient qu'un seul chapitre (niveau 3) mais peut contenir un ou plusieurs exrait.
+qui ne contient qu'un seul chapitre (niveau 3) mais peut contenir un ou plusieurs extraits.
On pourrait le représenter ainsi :
.. sourcecode:: none
@@ -87,12 +89,11 @@ présenter un seul chapitre de la structure globale.
Les bigtutos
------------
-Un bigtuto, si on repense à nos niveaux de structure, une un tutoriel dans lequel on peut avoir plusieurs
+Un bigtuto, si on repense à nos niveaux de structure, est un tutoriel dans lequel on peut avoir plusieurs
parties, chaque partie pouvant contenir plusieurs chapitres et chaque chapitre pouvant à leur tour
contenir plusieurs extraits.
-Les big tutos reprenent donc ici tous les éléments de la structure. Ce format est adapté aux tutoriels
-donc le contenu est assez conséquent, et demande beaucoup de structuration. On pourrait le représenter ainsi:
-
+Les bigtutos reprennent donc ici tous les éléments de la structure. Ce format est adapté aux tutoriels
+dont le contenu est assez conséquent, et demandent beaucoup de structuration. On pourrait le représenter ainsi :
.. sourcecode:: none
@@ -166,31 +167,32 @@ donc le contenu est assez conséquent, et demande beaucoup de structuration. On
Import de tutoriels
-========================
+===================
Zeste de Savoir permet d'importer des tutoriels provenant de sources extérieures. Deux formats d'import sont actuellement supportés.
Les archives zip
----------------
-Si vous avez commencé a rédiger un tutoriel via l'éditeur en ligne de Zeste de Savoir, vous avez téléchargé l'archive correspondate et vous avez fait des modifications sur les fichiers en hors-ligne, et vous souhaitez maintenant importer ces modifications sur le site ? Il suffit de faire une archive zip du répertoire dans lequel se trouve les fichiers de votre tutoriel et renseignez les deux champs relatif à l'import d'une archive, puis cliquez sur importer.
+
+Si vous avez commencé a rédiger un tutoriel via l'éditeur en ligne de Zeste de Savoir, vous avez téléchargé l'archive correspondante et vous avez fait des modifications sur les fichiers en hors-ligne, et vous souhaitez maintenant importer ces modifications sur le site. Il suffit de faire une archive zip du répertoire dans lequel se trouvent les fichiers de votre tutoriel et de renseigner les deux champs relatifs à l'import d'une archive, puis de cliquer sur importer.
.. figure:: images/import-archive.png
:align: center
.. attention::
- Le rajout d'une partie, d'un chapitre ou d'un tutoriel n'est pas encore supportés dans l'import. Le module n'importera que ce qui a été **modifié** dans les fichiers markdown.
+ Le rajout d'une partie, d'un chapitre ou d'un tutoriel n'est pas encore supporté dans l'import. Le module n'importera que ce qui a été **modifié** dans les fichiers markdown.
Le format .tuto
---------------
-Il s'agit du format dans lequel était exporté les tutoriels sur le SiteduZero. C'est un format de type xml. Cependant, pour qu'il soit
+Il s'agit du format dans lequel étaient exportés les tutoriels sur le Site du Zéro. C'est un format de type xml. Cependant, pour qu'il soit
importable sur ZdS il faut le transformer à l'aide d'un outil de conversion en semi-markdown. Si vous avez besoin d'importer un tutoriel
-de ce format, vous devez contacter le staff de Zeste de Savoir pour que vous fichier ``.tuto`` soit converti en semi markdown.
+de ce format, vous devez contacter le staff de Zeste de Savoir pour que votre fichier ``.tuto`` soit converti en semi markdown.
Vous aurez aussi besoin du pack d'images (au format zip) qui sont utilisés dans votre tutoriel.
-Il ne vous restera plus qu'a renseigner les champs relatifs à l'import de .tuto pour importer le votre.
+Il ne vous restera plus qu'à renseigner les champs relatifs à l'import de ``.tuto`` pour importer le vôtre.
.. figure:: images/import-tuto.png
:align: center
@@ -203,9 +205,9 @@ Cycle de vie des tutoriels
==========================
Quelque soit le type de tutoriel, le cycle de vie de celui-ci reste toujours le même.
-Un tutoriel peut-être rédigé par un ou plusieurs auteurs. Chaque modification sur le tutoriel
+Un tutoriel peut être rédigé par un ou plusieurs auteurs. Chaque modification sur le tutoriel
est conservée afin de pouvoir retrouver l'historique des modifications et éventuellement
-recupérer le contenu perdu. Lorsqu'un tutoriel est crée il rentre dans sa première étape.
+récupérer le contenu perdu. Lorsqu'un tutoriel est créé il rentre dans sa première étape.
Le brouillon
------------
@@ -213,30 +215,30 @@ Le brouillon
Le brouillon est la première étape du cycle de vie d'un tutoriel. Il donne toujours l'état
le plus récent du contenu d'un tutoriel vu par les auteurs. Chaque fois que le contenu du
tutoriel est modifié, c'est la version brouillon qui est mise à jour.
-La version brouillon est accessible uniquement pour les auteurs et validateurs d'un tutoriel,
-Si on souhaite donner un accès en lecture seule à nos écrit, il faut passer par la méthode
+La version brouillon est accessible uniquement pour les auteurs et validateurs d'un tutoriel.
+Si on souhaite donner un accès en lecture seule à nos écrits, il faut passer par la méthode
adéquate.
-La beta
+La bêta
-------
Lorsque les auteurs estiment que leur tutoriel est arrivé à un certain niveau de maturité, et qu'ils souhaitent
-recuillir les premiers retours de lecteurs, ils se doivent de mettre à disposition de la communauté le tutoriel en
-lecture seule. C'est le mode beta.
+recueillir les premiers retours de lecteurs, ils se doivent de mettre à disposition de la communauté le tutoriel en
+lecture seule. C'est le mode bêta.
-La procédure voudrait que lors de l'ouverture d'une beta, l'auteur crée un sujet dans le forum des tutoriels
-en beta, en postant le lien version la version beta du tutoriel.
+La procédure voudrait que lors de l'ouverture d'une bêta, l'auteur crée un sujet dans le forum des tutoriels
+en bêta, en postant le lien vers la version bêta du tutoriel.
.. attention::
- Le lien de la beta, peut être trouvé via votre profil utilisateur, vous devez recopier tout le lien avec la partie ``?version=blablabla``. Et pensez bien à modifier ce lien lorsque vous mettez à jour votre version beta.
+ Le lien de la bêta, peut être trouvé via votre profil utilisateur, vous devez recopier tout le lien avec la partie ``?version=blablabla``. Et pensez bien à modifier ce lien lorsque vous mettez à jour votre version bêta.
-En fait lorsqu'un tutoriel est en mode beta, il s'agit d'une version précise qui est mise
+En fait lorsqu'un tutoriel est en mode bêta, il s'agit d'une version précise qui est mise
dans ce mode. On peut continuer à mettre à jour la version brouillon pour rajouter de nouveaux chapitres
-à notre tutriel, pendant ce temps, la communauté lit une version figée de notre tutoriel. L'avantage étant que
+à notre tutoriel, pendant ce temps, la communauté lit une version figée de notre tutoriel. L'avantage étant que
si le tutoriel prend beaucoup de temps à lire, le lecteur n'a pas de mauvaise surprise de mise à jour
-pendant sa lecture. Les auteurs quant à eux doivent mettre à jour manuellement leur version beta et ainsi
-ils controllent pleinement ce qu'ils mettent à disposition des lecteurs.
+pendant sa lecture. Les auteurs quant à eux doivent mettre à jour manuellement leur version bêta et ainsi
+ils contrôlent pleinement ce qu'ils mettent à disposition des lecteurs.
La validation
-------------
@@ -244,63 +246,63 @@ La validation
Une fois que l'auteur a eu assez de retour sur son tutoriel, et qu'il estime qu'il est prêt à être publié,
il décide d'envoyer son tutoriel en validation.
-L'envoi en validation n'est pas définitif, dans le sens ou, vous pouvez à tout moment mettre à jour la version
-qui se trouve du coté des validateurs. Evitez d'en abuser tout de même, car, si un validateur commence à lire
+L'envoi en validation n'est pas définitif, dans le sens où, vous pouvez à tout moment mettre à jour la version
+qui se trouve du coté des validateurs. Évitez d'en abuser tout de même, car, si un validateur commence à lire
votre tutoriel, il devra recommencer son travail si vous faites une mise à jour dessus. Ce qui pourrait non seulement
-ralentir le processus de validation de votre tutoriel, mais décourager aussi le validateur. Donc un conseil a donner serait
+ralentir le processus de validation de votre tutoriel, mais décourager aussi le validateur. Donc un conseil à donner serait
de n'envoyer que du contenu sûr en validation.
-Comme pour la beta, la version brouillon du tutoriel peut continuer à être améliorée pendant que la version
-de validation reste figée. Auteurs et validateurs peuvent donc continuer à travailler chacun de son coté.
+Comme pour la bêta, la version brouillon du tutoriel peut continuer à être améliorée pendant que la version
+de validation reste figée. Auteurs et validateurs peuvent donc continuer à travailler chacun de son côté.
La publication
--------------
Une fois le contenu, lu et relu par l'équipe staff, le tutoriel est publié. Il faut bien préciser que le processus
de validation peut être assez long en fonction de la taille du tutoriel traité. Un tutoriel n'est pas obligé
-d'être publié à la suite d'une demande de validation, il peut aussi être rejeté. Dans tout les cas, un historique
+d'être publié à la suite d'une demande de validation, il peut aussi être rejeté. Dans tous les cas, un historique
de validation est disponible pour les membres du staff.
-La publication d'un tutoriel entraine la création d'export en plusieurs formats. On a les formats
+La publication d'un tutoriel entraîne la création d'export en plusieurs formats. On a les formats
- Markdown : disponible uniquement pour les membres du staff et les auteurs des tutoriels
- HTML
- PDF
- EPUB : format de lecture adapté aux liseuses
-- Archive : un export de l'archive contenant le dépot git du projet.
+- Archive : un export de l'archive contenant le dépôt git du projet.
-Pour différentes raisons, il se peut que l'export dans divers formats échoue. Dans ce cas, le lien de téléchargement n'est pas présenté. Un fichier de log sur le serveur enregistre les problèmes lié à l'export d'un format.
+Pour différentes raisons, il se peut que l'export dans divers formats échoue. Dans ce cas, le lien de téléchargement n'est pas présenté. Un fichier de log sur le serveur enregistre les problèmes liés à l'export d'un format.
Aujourd'hui il existe des bugs dans la conversion en PDF (blocs custom), qui devront être réglés plus tard avec la `ZEP 05 `_)
L'aspect technique
==================
-Le stockage dans la base de donnée
-----------------------------------
+Le stockage dans la base de données
+-----------------------------------
-Aujourd'hui la base de données est utilisée comme zone tampon, surtout parce que Django propose déjà des methodes
+Aujourd'hui la base de données est utilisée comme zone tampon, surtout parce que Django propose déjà des méthodes
d'enregistrement des objets en base de données de manière concurrentes et *thread safe*. L'idée étant de s'en
détacher à terme.
-La version stockée dans la base de données est le dernier état, c'est à dire l'état de la version en
+La version stockée dans la base de données est le dernier état, c'est-à-dire l'état de la version en
brouillon. Il ne faut donc pas aller chercher en base de données les informations pour les afficher.
Chaque tutoriel possède trois attributs principaux :
- sha_draft : le hash du commit de la version brouillon
-- sha_beta : le hash du commit de la version brouillon
+- sha_beta : le hash du commit de la version bêta
- sha_validation : le hash du commit de la version validation
- sha_public : le hash du commit de la version publique
On peut les voir comme des pointeurs sur chaque version, et le fait qu'ils soient stockés en base les rends
-plus accessibles. A terme aussi, on devrait pouvoir en faire des branches.
+plus accessibles. À terme aussi, on devrait pouvoir en faire des branches.
-Il faut aussi noter qu'on ne stocke pas le contenu (introduction, conclusion, extrait) directement en base de données, on stocke uniquement les chemin relatif vers le fichiers markdown qui contiennent le contenu.
+Il faut aussi noter qu'on ne stocke pas le contenu (introduction, conclusion, extraits) directement en base de données, on stocke uniquement les chemins relatifs vers les fichiers markdown qui contiennent le contenu.
-Les données versionnés
-----------------------
+Les données versionnées
+-----------------------
-Le module des tutoriels se base sur **git** pour versionner son contenu. Physiquement, nous avons un répertoire pour chaque tutoriel (point d'initialisation du dépot). A l'intérieur nous avons un répertoire par partie, et dans chaque partie, un répertoire par chapitre, et pour chaque chapitre, un fichier par extrait.
+Le module des tutoriels se base sur **git** pour versionner son contenu. Physiquement, nous avons un répertoire pour chaque tutoriel (point d'initialisation du dépôt). À l'intérieur nous avons un répertoire par partie, et dans chaque partie, un répertoire par chapitre, et pour chaque chapitre, un fichier par extrait.
Pour éviter les conflits dans les noms de fichier, le chemin vers un extrait aura souvent le modèle suivant :
@@ -311,17 +313,17 @@ Pour pouvoir versionner tout ceci, nous avons un fichier nommé ``masnifest.json
- Le titre du tutoriel, des parties, des chapitres et des extraits
- Le sous-titre du tutoriel
- La licence du tutoriel
-- Les divers chemin relatifs vers les fichiers markdown
+- Les divers chemins relatifs vers les fichiers markdown
-L'objectif étant d'arriver à tout versionner (catégories, ...) et de ne plus avoir à lire dans la base de donnée pour afficher quelque chose.
+L'objectif étant d'arriver à tout versionner (catégories, ...) et de ne plus avoir à lire dans la base de données pour afficher quelque chose.
Qu'en est-il des images ?
+++++++++++++++++++++++++
-Le versionning des images d'un tutoriel (celles qui font partie de la gallerie du tuto) continue a faire débat, et il a été décidé pour le moment de ne pas les versionner dans un premier temps, pour des raisons simples :
+Le versionning des images d'un tutoriel (celles qui font partie de la galerie du tuto) continue à faire débat, et il a été décidé pour le moment de ne pas les versionner dans un premier temps, pour des raisons simples :
-- versionner les images peut rendre très rapidement une archive lourde si l'auteur change beaucoup d'images, il va se trouver avec des images plus jamais utilisées qui traine dans son archive.
-- avoir besoin d'interroger le dépot à chaque fois pour lire les images peut rapidement devenir lourd pour la lecture.
+- versionner les images peut rendre très rapidement une archive lourde si l'auteur change beaucoup d'images, il va se trouver avec des images plus jamais utilisées qui traînent dans son archive.
+- avoir besoin d'interroger le dépôt à chaque fois pour lire les images peut rapidement devenir lourd pour la lecture.
Le parti a été pris de ne pas versionner les images qui sont stockées sur le serveur, ce n'est pas critique et on peut très bien travailler ainsi. Par contre, il faudra mieux y réfléchir pour une version 2 afin de proposer la rédaction totalement en mode hors ligne.
@@ -331,7 +333,7 @@ Quid des tutoriels publiés ?
Les tutoriels en *offline* sont tous versionnés, et sont dans le répertoire ``tutoriels_private``. Lorsqu'ils sont validés le traitement suivant est appliqué.
- On copie le dépôt du tutoriel dans le répertoire ``tutoriels_public``
-- On va chercher dans l'historique du dépot les fichiers correspondant à la version publique
+- On va chercher dans l'historique du dépôt les fichiers correspondant à la version publique
- On converti ces fichiers en html (en utilisant zMarkdown)
- On stocke les fichiers html sur le serveur.
diff --git a/doc/workflow.md b/doc/workflow.md
index 86eb432d54..4c49c6e507 100644
--- a/doc/workflow.md
+++ b/doc/workflow.md
@@ -1,4 +1,4 @@
-Cette page détaille le _workflow_ utilisé sur Zeste de Savoir. Elle est là surtout pour satisfaire votre curiosité, à moins d'avoir les droits de faire une Mise En Production (MEP). La [page de contribution](CONTRIBUTING.md) devrait répondre à vos questions quant au processus de développement.
+Cette page détaille le _workflow_ utilisé sur Zeste de Savoir. Elle est là surtout pour satisfaire votre curiosité, à moins d'avoir les droits de faire une Mise En Production (MEP). La [page de contribution](../CONTRIBUTING.md) devrait répondre à vos questions quant au processus de développement.
Ce _workflow_ est très fortement basé sur le [Git flow](http://nvie.com/posts/a-successful-git-branching-model/).
@@ -25,7 +25,7 @@ L'idée générale est très simple :
**Où peut-on trouver les détails pratiques ?**
-Tous ces détails sont [dans la page de contribution](CONTRIBUTING.md). On y trouve entre autres les recommendations en terme de PR ou de messages de commits.
+Tous ces détails sont [dans la page de contribution](../CONTRIBUTING.md). On y trouve entre autres les recommendations en terme de PR ou de messages de commits.
**Qu'est-ce qu'une "QA légère"** ?
diff --git a/fixtures/users.yaml b/fixtures/users.yaml
index cb659b80c3..445195c2da 100644
--- a/fixtures/users.yaml
+++ b/fixtures/users.yaml
@@ -63,9 +63,9 @@
- model: auth.user
pk: 6
fields:
- first_name: Auteur externe
+ first_name: External
last_name: User
- username: Auteur externe
+ username: external
password: pbkdf2_sha256$12000$DWHNLpO3jXIv$MrnMZblGu1Q+xemP4XK2keLtbyfkRXxczPTLhSJ0AAM=
is_superuser: False
- model: member.Profile
diff --git a/package.json b/package.json
index 7461686ffc..c7af7438d6 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,6 @@
"gulp": "^3.7.0",
"gulp-autoprefixer": "0.0.9",
"gulp-cache": "^0.2.1",
- "gulp-clean": "^0.3.0",
"gulp-compass": "^1.1.9",
"gulp-concat": "^2.2.0",
"gulp-filter": "^1.0.0",
diff --git a/requirements.txt b/requirements.txt
index 99841e8e71..7c05977f9c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,36 +1,29 @@
-django>=1.6,<1.7
-bleach==1.2.2
-pygments
+# Implicit dependencies (facutlative dependencies of dependencies)
+pysolr==3.2.0
+pygments==1.6
+
+# Explicit dependencies (references in code)
+django==1.6.8
coverage==3.7.1
-south
-pytz==2013d
-PyYAML==3.11
-defusedxml
-django-dynamic-fixture
-django-crispy-forms==1.3.2
-django-simple-captcha
-django-email-obfuscator==0.1.2
-django-debug-toolbar
-djangorestframework
-django-filter
-django-rest-swagger
-django-oauth2-provider
-django-guardian
-django-haystack
-django-model-utils
-django-munin==0.1.4
-python-memcached
-lxml
-pygal
-httplib2
-pysolr
-factory-boy==2.2.1
-django-discover-runner==0.4
+south==1.0
+django-crispy-forms==1.4.0
+django-email-obfuscator==0.1.3
+django-haystack==2.3.1
+django-model-utils==2.2
+django-munin==0.1.5
+python-memcached==1.53
+lxml==3.4.0
+pygal==1.5.1
+factory-boy==2.4.1
pygeoip==0.3.1
-pillow
+pillow==2.6.1
https://github.com/zestedesavoir/GitPython/archive/0.3.2-RC1.z2.zip
https://github.com/zestedesavoir/Python-ZMarkdown/archive/2.4.1-zds.10.zip
-flake8==2.2.3
-autopep8
-easy-thumbnails
-sphinx
+easy-thumbnails==2.2
+
+# Development tools
+PyYAML==3.11
+django-debug-toolbar==1.2.1
+flake8==2.2.5
+autopep8==1.0.4
+sphinx==1.2.3
diff --git a/server/deploy.sh b/server/deploy.sh
index d58cf51956..8a40bf867c 100644
--- a/server/deploy.sh
+++ b/server/deploy.sh
@@ -17,6 +17,15 @@ if [ "$#" -ne 1 ]; then
echo "Usage: $0 " >&2
exit 1
fi
+
+read -p "Did you run specific tasks for this version as described in update.md? " -r
+echo # move to a new line
+if [[ ! $REPLY =~ ^[Yy]$ ]]
+then
+ echo "Do it, now!"
+ exit 1
+fi
+
cd /opt/zdsenv/ZesteDeSavoir/
# Maintenance mode
diff --git a/templates/500.html b/templates/500.html
index e0a6a3c115..9e29e66fda 100644
--- a/templates/500.html
+++ b/templates/500.html
@@ -30,6 +30,12 @@
Si vous voyez cette page, c'est que vous avez trouvé un bug. Merci
de rapporter cette erreur en expliquant comment vous avez procédé
- pour tomber sur cette page.
+ pour tomber sur cette page en créant un sujet sur
+
+ le forum "Bugs et Suggestions". Merci de votre aide !
+
{% endblock %}
\ No newline at end of file
diff --git a/templates/article/member/view.html b/templates/article/member/view.html
index e6d07e1451..ec9593c1d0 100644
--- a/templates/article/member/view.html
+++ b/templates/article/member/view.html
@@ -47,7 +47,7 @@