Unverified Commit 4e7bdb92 authored by Nicolas Widart's avatar Nicolas Widart

Merge branch '3.0'

* 3.0:
  Preparing 3.6.0 release
  Adding changelogs
  multiple selector
  Added Image Attribute
  i18nFile & normalFile added two function
  Fixed a bug that checked the last radio even if wasn't the right one
  Pass  to the view
  Add canonical and metadata for pages
  Revert "Add canonical url and alternate links for each language available"
  Add canonical url and alternate links for each language available
  This check if the request page match the locale and the slug. If do not match just do a 301 redirect so the user can see the correct page and the correct url, good for seo purpose.
  In case of accepts json then instead of redirecting sending unauthorised response

# Conflicts:
#	Modules/Core/Foundation/AsgardCms.php
#	Modules/Core/changelog.yml
#	Modules/Media/changelog.yml
#	Modules/Page/changelog.yml
#	Modules/User/changelog.yml
parents 237632e8 b16c521e
...@@ -49,7 +49,7 @@ class Authorization ...@@ -49,7 +49,7 @@ class Authorization
*/ */
private function handleUnauthorizedRequest(Request $request, $permission) private function handleUnauthorizedRequest(Request $request, $permission)
{ {
if ($request->ajax()) { if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', Response::HTTP_FORBIDDEN); return response('Unauthorized.', Response::HTTP_FORBIDDEN);
} }
if ($request->user() === null) { if ($request->user() === null) {
......
...@@ -2,8 +2,11 @@ url: https://github.com/AsgardCms/Platform ...@@ -2,8 +2,11 @@ url: https://github.com/AsgardCms/Platform
versions: versions:
"@unreleased": "@unreleased":
added: added:
- New Hungarian translations
- Laravel 5.6 compatibility - Laravel 5.6 compatibility
"3.6.0":
added:
- New Hungarian translations
- new <code>i18nFile</code> & <code>normalFile</code> macros
changed: changed:
- Updating Dutch translations - Updating Dutch translations
- Fix Carbon locale - Fix Carbon locale
......
...@@ -176,6 +176,32 @@ Form::macro('i18nSelect', function ($name, $title, ViewErrorBag $errors, $lang, ...@@ -176,6 +176,32 @@ Form::macro('i18nSelect', function ($name, $title, ViewErrorBag $errors, $lang,
return new HtmlString($string); return new HtmlString($string);
}); });
Form::macro('i18nFile', function ($name, $title, ViewErrorBag $errors, $lang, $object = null, array $options = []) {
if (array_key_exists('multiple', $options)) {
$nameForm = "{$lang}[$name][]";
} else {
$nameForm = "{$lang}[$name]";
}
$options = array_merge(['class' => 'form-control'], $options);
$string = "<div class='form-group " . ($errors->has($lang . '.' . $name) ? ' has-error' : '') . "'>";
$string .= "<label for='$nameForm'>$title</label>";
if (is_object($object)) {
$currentData = $object->hasTranslation($lang) ? $object->translate($lang)->{$name} : '';
} else {
$currentData = false;
}
$string .= Form::file("{$lang}[{$name}]",$options);
$string .= $errors->first("{$lang}.{$name}", '<span class="help-block">:message</span>');
$string .= '</div>';
return new HtmlString($string);
});
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Standard fields | Standard fields
...@@ -346,6 +372,25 @@ Form::macro('normalSelect', function ($name, $title, ViewErrorBag $errors, array ...@@ -346,6 +372,25 @@ Form::macro('normalSelect', function ($name, $title, ViewErrorBag $errors, array
return new HtmlString($string); return new HtmlString($string);
}); });
Form::macro('normalFile', function ($name, $title, ViewErrorBag $errors, $object = null, array $options = []) {
$options = array_merge(['class' => 'form-control', 'placeholder' => $title,'multiple'=>'multiple'], $options);
$string = "<div class='form-group " . ($errors->has($name) ? ' has-error' : '') . "'>";
$string .= Form::label($name, $title);
if (is_object($object)) {
$currentData = $object->{$name} ?: '';
} else {
$currentData = null;
}
$string .= Form::file($name,$options);
$string .= $errors->first($name, '<span class="help-block">:message</span>');
$string .= '</div>';
return new HtmlString($string);
});
Response::macro('csv', function ($file, $filename, $status = 200, $headers = []) { Response::macro('csv', function ($file, $filename, $status = 200, $headers = []) {
return response($file, $status, array_merge([ return response($file, $status, array_merge([
'Content-Type' => 'application/csv', 'Content-Type' => 'application/csv',
......
<template>
<div>
<label class="el-form-item__label">{{ getFieldLabel() }}</label>
<div class="jsThumbnailImageWrapper jsSingleThumbnailWrapper" v-if="hasSelectedMedia" >
<figure v-for="media in this.selectedMedia" :key="media.id">
<img :src="media.small_thumb" alt="" v-if="media.is_image"/>
<i :class="`fa ${media.fa_icon}`" style="font-size: 60px;" v-if="! media.is_image"></i>
<span v-if="! media.is_image" style="display:block;">{{ media.filename }}</span>
<span class="el-icon-error remove-media" @click="unSelectMedia(media.id)"></span>
</figure>
<div class="clearfix"></div>
</div>
<div>
<el-button type="button" @click="dialogVisible = true">{{ trans('media.Browse') }}</el-button>
</div>
<el-dialog
:visible.sync="dialogVisible"
fullscreen
:before-close="handleClose">
<media-list single-modal :event-name="this.eventName"></media-list>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">{{ trans('core.button.cancel') }}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import axios from 'axios';
import UploadZone from '../../../../Media/Assets/js/components/UploadZone.vue';
import MediaList from '../../../../Media/Assets/js/components/MediaList.vue';
import StringHelpers from '../../../../Core/Assets/js/mixins/StringHelpers.vue';
export default {
mixins: [StringHelpers],
props: {
zone: { type: String, required: true },
entity: { type: String, required: true },
entityId: { default: null },
label: { type: String },
},
components: {
'upload-zone': UploadZone,
'media-list': MediaList,
},
watch: {
entityId() {
if (this.entityId) {
this.fetchMedia();
}
},
},
data() {
return {
dialogVisible: false,
selectedMedia: [],
eventName: '',
};
},
computed: {
hasSelectedMedia() {
return this.selectedMedia !== undefined && !_.isEmpty(this.selectedMedia);
},
},
methods: {
handleClose(done) {
done();
},
unSelectMedia(id) {
this.selectedMedia = _.reject(this.selectedMedia, media => media.id === id);
this.$emit('fileUnselected', { id, zone: this.zone });
},
fetchMedia() {
axios.get(route('api.media.get-by-zone-and-entity', {
zone: this.zone,
entity: this.entity,
entity_id: this.entityId,
}))
.then((response) => {
this.selectedMedia = response.data.data;
_.forEach(this.selectedMedia, (file) => {
this.$emit('multipleFileSelected', { id: file.id, zone: this.zone });
});
});
},
getFieldLabel() {
return this.label || this.ucwords(this.zone.replace('_', ' '));
},
makeId() {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 5; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); }
return text;
},
},
mounted() {
if (this.entityId) {
this.fetchMedia();
}
this.eventName = `fileWasSelected${this.makeId()}${Math.floor(Math.random() * 999999)}`;
this.$events.listen(this.eventName, (mediaData) => {
if (_.find(this.selectedMedia, mediaData) === undefined) {
if (!this.selectedMedia) this.selectedMedia = [];
this.selectedMedia.push(mediaData);
this.$emit('multipleFileSelected', _.merge(mediaData, { zone: this.zone }));
}
});
},
};
</script>
<style>
.remove-media{
position: absolute;
top: 5px;
left: 5px;
color: #FA5555;
}
</style>
\ No newline at end of file
export default {
methods: {
selectMultipleFile(event, model) {
if (!this[model].medias_multi) {
this[model].medias_multi = {};
}
if (!this[model].medias_multi[event.zone]) {
this[model].medias_multi[event.zone] = { files: [] };
}
this[model].medias_multi[event.zone].files.push(event.id);
},
unselectFile(event, model) {
if (!this[model].medias_multi) {
this[model].medias_multi = {};
}
if (!this[model].medias_multi[event.zone]) {
this[model].medias_multi[event.zone] = { files: [] };
if (this.$refs['multiple-media'] !== undefined && this.$refs['multiple-media'].selectedMedia !== undefined && !_.isEmpty(this.$refs['multiple-media'].selectedMedia)) {
_.forEach(this.$refs['multiple-media'].selectedMedia, (file, key) => {
this[model].medias_multi[event.zone].files.push(file.id);
});
}
}
this[model].medias_multi[event.zone].files = _.reject(this[model].medias_multi[event.zone].files, media => media === event.id);
},
},
};
\ No newline at end of file
...@@ -93,6 +93,24 @@ class MediaController extends Controller ...@@ -93,6 +93,24 @@ class MediaController extends Controller
return new MediaTransformer($file); return new MediaTransformer($file);
} }
/**
* Get a media collection by zone and entity object. Require some params that were passed to request: entity (Full class name of entity), entity_id and zone
*
* @param Request $request
* @return JsonResponse|\Illuminate\Http\Resources\Json\AnonymousResourceCollection
*/
public function getByZoneEntity(Request $request)
{
$entityName = (string)$request->get('entity');
$entityModel = new $entityName;
$entity = $entityModel::find($request->get('entity_id'));
if ($entity && in_array('Modules\Media\Support\Traits\MediaRelation', class_uses($entity)) && $entity->files()->count()) {
$files = $this->file->findMultipleFilesByZoneForEntity($request->get('zone'), $entity);
return MediaTransformer::collection($files);
}
return response()->json(['data' => null]);
}
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
......
...@@ -70,6 +70,11 @@ $router->group(['middleware' => 'api.token'], function (Router $router) { ...@@ -70,6 +70,11 @@ $router->group(['middleware' => 'api.token'], function (Router $router) {
'as' => 'api.media.find-first-by-zone-and-entity', 'as' => 'api.media.find-first-by-zone-and-entity',
]); ]);
$router->get('media/get-by-zone-and-entity', [
'uses' => 'MediaController@getByZoneEntity',
'as' => 'api.media.get-by-zone-and-entity',
]);
$router->get('media/{media}', [ $router->get('media/{media}', [
'uses' => 'MediaController@find', 'uses' => 'MediaController@find',
'as' => 'api.media.media.find', 'as' => 'api.media.media.find',
......
...@@ -3,6 +3,9 @@ versions: ...@@ -3,6 +3,9 @@ versions:
"@unreleased": "@unreleased":
added: added:
- Laravel 5.6 compatibility - Laravel 5.6 compatibility
"3.6.0":
added:
- New MediaMuliple component for vuejs projects
"3.5.1": "3.5.1":
changed: changed:
- Modify media controller to use Imagy non-statically - Modify media controller to use Imagy non-statically
......
...@@ -77,4 +77,15 @@ class Page extends Model implements TaggableInterface ...@@ -77,4 +77,15 @@ class Page extends Model implements TaggableInterface
#i: No relation found, return the call to parent (Eloquent) to handle it. #i: No relation found, return the call to parent (Eloquent) to handle it.
return parent::__call($method, $parameters); return parent::__call($method, $parameters);
} }
public function getImageAttribute()
{
$thumbnail = $this->files()->where('zone', 'image')->first();
if ($thumbnail === null) {
return '';
}
return $thumbnail;
}
} }
...@@ -38,9 +38,17 @@ class PublicController extends BasePublicController ...@@ -38,9 +38,17 @@ class PublicController extends BasePublicController
$this->throw404IfNotFound($page); $this->throw404IfNotFound($page);
$currentTranslatedPage = $page->getTranslation(locale());
if ($slug !== $currentTranslatedPage->slug) {
return redirect()->to($currentTranslatedPage->locale . '/' . $currentTranslatedPage->slug, 301);
}
$template = $this->getTemplateForPage($page); $template = $this->getTemplateForPage($page);
return view($template, compact('page')); $alternate = $this->getAlternateMetaData($page);
return view($template, compact('page', 'alternate'));
} }
/** /**
...@@ -54,7 +62,9 @@ class PublicController extends BasePublicController ...@@ -54,7 +62,9 @@ class PublicController extends BasePublicController
$template = $this->getTemplateForPage($page); $template = $this->getTemplateForPage($page);
return view($template, compact('page')); $alternate = $this->getAlternateMetaData($page);
return view($template, compact('page', 'alternate'));
} }
/** /**
...@@ -95,4 +105,23 @@ class PublicController extends BasePublicController ...@@ -95,4 +105,23 @@ class PublicController extends BasePublicController
$this->app->abort('404'); $this->app->abort('404');
} }
} }
/**
* Create a key=>value array for alternate links
*
* @param $page
*
* @return array
*/
private function getAlternateMetaData($page)
{
$translations = $page->getTranslationsArray();
$alternate = [];
foreach ($translations as $locale => $data) {
$alternate[$locale] = $data['slug'];
}
return $alternate;
}
} }
...@@ -3,10 +3,13 @@ versions: ...@@ -3,10 +3,13 @@ versions:
"@unreleased": "@unreleased":
added: added:
- Laravel 5.6 compatibility - Laravel 5.6 compatibility
"3.5.2": "3.6.0":
added:
- Add canonical and metadata for pages
changed: changed:
- Don't show draft pages - Don't show draft pages
- Throw a 404 if page status is disabled - Throw a 404 if page status is disabled
- Fixed a bug that checked the last radio even if wasn't the right one
"3.5.2": "3.5.2":
changed: changed:
- Adding support for UTF-8 slug for non ASCII characters (Chinese etc.) - Adding support for UTF-8 slug for non ASCII characters (Chinese etc.)
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
name="{{ $settingName }}" name="{{ $settingName }}"
type="radio" type="radio"
class="flat-blue" class="flat-blue"
{{ isset($dbSettings[$settingName]) && (bool)$dbSettings[$settingName]->plainValue == $value ? 'checked' : '' }} {{ isset($dbSettings[$settingName]) && $dbSettings[$settingName]->plainValue == $value ? 'checked' : '' }}
value="{{ $value }}" /> value="{{ $value }}" />
{{ trans($optionName) }} {{ trans($optionName) }}
</label> </label>
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
name="{{ $settingName . "[$lang]" }}" name="{{ $settingName . "[$lang]" }}"
type="radio" type="radio"
class="flat-blue" class="flat-blue"
{{ isset($dbSettings[$settingName]) && (bool)$oldValue == $value ? 'checked' : '' }} {{ isset($dbSettings[$settingName]) && $oldValue == $value ? 'checked' : '' }}
value="{{ $value }}" /> value="{{ $value }}" />
{{ trans($optionName) }} {{ trans($optionName) }}
</label> </label>
......
url: https://github.com/AsgardCms/Platform url: https://github.com/AsgardCms/Platform
versions: versions:
"@unreleased": "3.6.0":
added: added:
- New <code>@hasSetting()</code> and <code>@endHasSetting</code> directives - New <code>@hasSetting()</code> and <code>@endHasSetting</code> directives
- Laravel 5.6 compatibility - Laravel 5.6 compatibility
......
...@@ -3,9 +3,10 @@ versions: ...@@ -3,9 +3,10 @@ versions:
"@unreleased": "@unreleased":
added: added:
- Laravel 5.6 compatibility - Laravel 5.6 compatibility
changed: "3.6.0":
- Adding a test the user token is correctly generated changed:
- Fix creation of an activated user - Adding a test the user token is correctly generated
- Fix creation of an activated user
"3.5.1": "3.5.1":
changed: changed:
- Fix issue 467 id() method checks null instead of false - Fix issue 467 id() method checks null instead of false
......
...@@ -3,12 +3,14 @@ ...@@ -3,12 +3,14 @@
<head lang="{{ LaravelLocalization::setLocale() }}"> <head lang="{{ LaravelLocalization::setLocale() }}">
<meta charset="UTF-8"> <meta charset="UTF-8">
@section('meta') @section('meta')
<meta name="description" content="@setting('core::site-description')" /> <meta name="description" content="@setting('core::site-description')"/>
@show @show
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title> <title>@section('title')@setting('core::site-name')@show</title>
@section('title')@setting('core::site-name')@show @foreach($alternate as $alternateLocale=>$alternateSlug)
</title> <link rel="alternate" hreflang="{{$alternateLocale}}" href="{{url($alternateLocale.'/'.$alternateSlug)}}">
@endforeach
<link rel="canonical" href="{{url()->current()}}" />
<link rel="shortcut icon" href="{{ Theme::url('favicon.ico') }}"> <link rel="shortcut icon" href="{{ Theme::url('favicon.ico') }}">
{!! Theme::style('css/main.css') !!} {!! Theme::style('css/main.css') !!}
...@@ -30,7 +32,7 @@ ...@@ -30,7 +32,7 @@
@yield('scripts') @yield('scripts')
<?php if (Setting::has('core::analytics-script')): ?> <?php if (Setting::has('core::analytics-script')): ?>
{!! Setting::get('core::analytics-script') !!} {!! Setting::get('core::analytics-script') !!}
<?php endif; ?> <?php endif; ?>
@stack('js-stack') @stack('js-stack')
</body> </body>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment