How can I recreate the folder layout from Telegram in SwiftUI? - layout

I would like to recreate this layout, which is the layout of the Chat section from the Telegram app. The only thing I don't need to add is the horizontal scrolling. This is what I was able to recreate for now, but there's still too much space between the navigation bar and the folders list. How do I reduce it to be as small as the one in the Telegram app?
Here's my code:
import SwiftUI
struct ContentView: View {
#State var showMainView = true
#State var showFirstView = false
#State var showSecondView = false
var allItems = ["Italy", "Germany", "France", "Ireland", "Austria", "Sweden", "Norway", "Russia", "Ukraine", "Spain", "Portugal"]
var body: some View {
NavigationView {
VStack {
topMenu
Form {
if showMainView {
ForEach(allItems, id: \.self) { itms in
Text(itms)
}
}
if showFirstView {
ForEach(allItems.filter({ $0.first == "F" || $0.first == "G" }), id: \.self) { itms in
Text(itms)
}
}
if showSecondView {
Text("This folder is still empty!")
}
}
}
.toolbar {
ToolbarItem(placement: .principal) {
HStack {
Text("My List")
Spacer()
}
.padding(.leading)
}
}
.navigationBarItems(trailing: showMainView == true ? editButton : nil)
}
}
var topMenu : some View {
HStack(spacing: 15) {
Button {
showMainView = true
showFirstView = false
showSecondView = false
} label: {
if showMainView {
Text("All")
.underline()
} else {
Text("All")
}
}
.buttonStyle(BorderlessButtonStyle())
.foregroundColor(showMainView == true ? .green : .black)
Button {
showMainView = false
showFirstView = true
showSecondView = false
} label: {
if showFirstView {
Text("Folder 1")
.underline()
} else {
Text("Folder 1")
}
}
.buttonStyle(BorderlessButtonStyle())
.foregroundColor(showFirstView == true ? .green : .black)
Button {
showMainView = false
showFirstView = false
showSecondView = true
} label: {
if showSecondView {
Text("Folder 2")
.underline()
} else {
Text("Folder 2")
}
}
.buttonStyle(BorderlessButtonStyle())
.foregroundColor(showSecondView == true ? .green : .black)
Spacer()
}
.padding(.leading)
}
var editButton : some View {
Button {
} label: {
Text("Edit")
.foregroundColor(.green)
}
}
}
Thanks!

Related

SwiftUI - Changing struct data set in ForEach?

I'm new to programming and SwiftUI and I am making this app where users can select these buttons labeled A-D. They may choose more than 1, and I am hoping that when they click on the button, the background colour will change from grey to green. However, if I replace "// Here" in the code at the bottom with
Data.Selected = true
Data.Colour = .green
I get an error saying "Cannot assign to property: 'Data' is a 'let' constant". I understand what that means, but I don't know how to change Data to var. I tried typing var in front of "Data in" but I got this error instead "Consecutive statements on a line must be separated by ';'". Is there anyway I can directly modify Data/ButtonsData? Or is there a workaround?
struct Buttons: Hashable {
var Crit: String
var Selected: Bool
var Colour: Color
}
var ButtonsData = [
Buttons(Crit: "A", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "B", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "C", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "D", Selected: false, Colour: Color(.systemGray4))
]
struct CritView: View {
#Binding var CritBoard: Bool
#Binding var BackgroundColor: Color
var body: some View {
ZStack(alignment: .topLeading) {
ScrollView(.vertical, showsIndicators: false) {
HStack(spacing: 15) {
ForEach(ButtonsData, id: \.self) { Data in
Button(action: {
// HERE
}) {
Text(Data.Crit)
.font(.system(size: 30))
}
.frame(width: 65, height: 55)
.background(Data.Colour)
.cornerRadius(10)
}
}
.padding(.top, 50)
}
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/8)
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
.background(Color(.white))
.cornerRadius(25)
Button(action: {
self.CritBoard.toggle()
self.BackgroundColor = .white
}) {
Image(systemName: "xmark").foregroundColor(.black)
}.padding(25)
}
}
}
Here is possible solution - create array of index/value tuples and modify your data in original container by index:
ForEach(Array(ButtonsData.enumerated()), id: \.element) { i, Data in
Button(action: {
ButtonsData[i].Selected = true
ButtonsData[i].Colour = .green
}) {
Text(Data.Crit)
.font(.system(size: 30))
}
.frame(width: 65, height: 55)
.background(Data.Colour)
.cornerRadius(10)
}
Well, I don't think a lot of people would actually have the same/similar problem but this is my working code. The code uses both #Aspersi's answer as well as the code in a Hacking with Swift article. (it might not be the most simplified code, but it works right now at least)
ForEach(Array(ButtonsData.enumerated()), id: \.element) { i, Data in
Button(action: {
self.AllData[i].Selected.toggle()
if self.AllData[i].Selected == true {
self.AllData[i].Colour = .green
} else {
self.AllData[i].Colour = Color(.systemGray4)
}
}) {
Text(Data.Crit)
.font(.system(size: 30))
}
.frame(width: 65, height: 55)
.background(self.AllData[i].Colour)
.cornerRadius(10)
}
Full code below
struct Buttons: Hashable {
var Crit: String
var Selected: Bool = false
var Colour: Color
}
var ButtonsData = [
Buttons(Crit: "A", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "B", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "C", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "D", Selected: false, Colour: Color(.systemGray4))
]
struct CritView: View {
#Binding var CritBoard: Bool
#Binding var BackgroundColor: Color
#State private var AllData = ButtonsData
var body: some View {
ZStack(alignment: .topLeading) {
ScrollView(.vertical, showsIndicators: false) {
HStack(spacing: 15) {
ForEach(Array(ButtonsData.enumerated()), id: \.element) { I, Data in
Button(action: {
self.AllData[i].Selected.toggle()
if self.AllData[i].Selected == true {
self.AllData[i].Colour = .green
} else {
self.AllData[i].Colour = Color(.systemGray4)
}
}) {
Text(Data.Crit)
.font(.system(size: 30))
}
.frame(width: 65, height: 55)
.background(self.AllData[i].Colour)
.cornerRadius(10)
}
}
.padding(.top, 50)
}
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/8)
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
.background(Color(.white))
.cornerRadius(25)
Button(action: {
self.CritBoard.toggle()
self.BackgroundColor = .white
}) {
Image(systemName: "xmark").foregroundColor(.black)
}.padding(25)
}
}
}

Get child component reference in parent component

I am new to Angular and stuck on an issue. Anyone can help me to figure out that what I am doing wrong in parent component. I am not able to get the child component reference in parent. I already followed following reference but not succeeded.
Angular 2 #ViewChild annotation returns undefined
https://expertcodeblog.wordpress.com/2018/01/12/angular-resolve-error-viewchild-annotation-returns-undefined/
Parent:
import { Component, OnInit, OnDestroy, ViewChild, HostListener, AfterViewInit, ViewChildren, QueryList } from '#angular/core';
import { Router, NavigationEnd, NavigationStart } from '#angular/router';
import { NavItem, NavItemType } from '../../md/md.module';
import { Subscription } from 'rxjs/Subscription';
import { Location, LocationStrategy, PathLocationStrategy, PopStateEvent } from '#angular/common';
import 'rxjs/add/operator/filter';
import { NavbarComponent } from '../../shared/navbar/navbar.component';
import PerfectScrollbar from 'perfect-scrollbar';
import { ChatService } from 'app/services/chat.service';
import swal from 'sweetalert2';
import { JitsiService } from 'app/services/jitsi.service';
import { UserService } from 'app/services/user.service';
import { ConferenceStudentComponent } from 'app/conference-student/conference-student.component';
declare const $: any;
#Component({
selector: 'app-layout',
templateUrl: './admin-layout.component.html'
})
export class AdminLayoutComponent implements OnInit, AfterViewInit {
public navItems: NavItem[];
private _router: Subscription;
private lastPoppedUrl: string;
private yScrollStack: number[] = [];
url: string;
location: Location;
#ViewChild('sidebar') sidebar: any;
#ViewChild(NavbarComponent) navbar: NavbarComponent;
#ViewChildren(ConferenceStudentComponent) stuConf: QueryList<ConferenceStudentComponent>;
constructor( private router: Router, location: Location,
private chatService: ChatService,
private jitsiService: JitsiService,
private userService: UserService
) {
this.location = location;
this.chatService.callVisibilityChange
.subscribe(callFrom => {
console.log('admin layout call from', callFrom);
if (callFrom) {
this.userService.getLoggedUserDetail()
.subscribe(loggedUser => {
if (!loggedUser) {
console.log(`Invalid token, logged user data not fetched`);
return false;
}
this.userService.getUser(callFrom['fromUser'])
.subscribe(otherUser => {
swal({
title: `${otherUser['fullName']} is calling`,
text: `Click on accept to join session`,
type: `info`,
showCancelButton: true,
cancelButtonColor: `#d33`,
cancelButtonText: `reject`,
confirmButtonColor: `#3085d6`,
confirmButtonText: `accept`
}).then((result) => {
if (result.value) {
const jitsiSessionData = {
loggedUser,
otherUser,
roomName: callFrom['roomName']
}
this.router.navigateByUrl(`/conference-student/${otherUser['_id']}`);
window.setTimeout(() => this.jitsiService.joinSession(jitsiSessionData), 10000);
} else {
console.log('user select rejected');
this.chatService.jitsiCallReject(otherUser._id, loggedUser._id, callFrom['roomName']);
}
})
});
});
}
});
}
ngOnInit() {
const elemMainPanel = <HTMLElement>document.querySelector('.main-panel');
const elemSidebar = <HTMLElement>document.querySelector('.sidebar .sidebar-wrapper');
this.location.subscribe((ev:PopStateEvent) => {
this.lastPoppedUrl = ev.url;
});
this.router.events.subscribe((event:any) => {
if (event instanceof NavigationStart) {
if (event.url != this.lastPoppedUrl)
this.yScrollStack.push(window.scrollY);
} else if (event instanceof NavigationEnd) {
if (event.url == this.lastPoppedUrl) {
this.lastPoppedUrl = undefined;
window.scrollTo(0, this.yScrollStack.pop());
}
else
window.scrollTo(0, 0);
}
});
this._router = this.router.events.filter(event => event instanceof NavigationEnd).subscribe((event: NavigationEnd) => {
elemMainPanel.scrollTop = 0;
elemSidebar.scrollTop = 0;
});
const html = document.getElementsByTagName('html')[0];
if (window.matchMedia(`(min-width: 960px)`).matches && !this.isMac()) {
let ps = new PerfectScrollbar(elemMainPanel);
ps = new PerfectScrollbar(elemSidebar);
html.classList.add('perfect-scrollbar-on');
}
else {
html.classList.add('perfect-scrollbar-off');
}
this._router = this.router.events.filter(event => event instanceof NavigationEnd).subscribe((event: NavigationEnd) => {
this.navbar.sidebarClose();
});
this.navItems = [
{ type: NavItemType.NavbarLeft, title: 'Dashboard', iconClass: 'fa fa-dashboard' },
{
type: NavItemType.NavbarRight,
title: '',
iconClass: 'fa fa-bell-o',
numNotifications: 5,
dropdownItems: [
{ title: 'Notification 1' },
{ title: 'Notification 2' },
{ title: 'Notification 3' },
{ title: 'Notification 4' },
{ title: 'Another Notification' }
]
},
{
type: NavItemType.NavbarRight,
title: '',
iconClass: 'fa fa-list',
dropdownItems: [
{ iconClass: 'pe-7s-mail', title: 'Messages' },
{ iconClass: 'pe-7s-help1', title: 'Help Center' },
{ iconClass: 'pe-7s-tools', title: 'Settings' },
'separator',
{ iconClass: 'pe-7s-lock', title: 'Lock Screen' },
{ iconClass: 'pe-7s-close-circle', title: 'Log Out' }
]
},
{ type: NavItemType.NavbarLeft, title: 'Search', iconClass: 'fa fa-search' },
{ type: NavItemType.NavbarLeft, title: 'Account' },
{
type: NavItemType.NavbarLeft,
title: 'Dropdown',
dropdownItems: [
{ title: 'Action' },
{ title: 'Another action' },
{ title: 'Something' },
{ title: 'Another action' },
{ title: 'Something' },
'separator',
{ title: 'Separated link' },
]
},
{ type: NavItemType.NavbarLeft, title: 'Log out' }
];
}
ngAfterViewInit() {
this.runOnRouteChange();
this.stuConf.changes.subscribe((comp: QueryList<ConferenceStudentComponent>) => {
console.log(`student component`, comp);
})
}
public isMap() {
if (this.location.prepareExternalUrl(this.location.path()) === '/maps/fullscreen') {
return true;
} else {
return false;
}
}
runOnRouteChange(): void {
if (window.matchMedia(`(min-width: 960px)`).matches && !this.isMac()) {
const elemSidebar = <HTMLElement>document.querySelector('.sidebar .sidebar-wrapper');
const elemMainPanel = <HTMLElement>document.querySelector('.main-panel');
let ps = new PerfectScrollbar(elemMainPanel);
ps = new PerfectScrollbar(elemSidebar);
ps.update();
}
}
isMac(): boolean {
let bool = false;
if (navigator.platform.toUpperCase().indexOf('MAC') >= 0 || navigator.platform.toUpperCase().indexOf('IPAD') >= 0) {
bool = true;
}
return bool;
}
}
Child:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, ParamMap } from '#angular/router';
import '../../vendor/jitsi/external_api.js';
import { JitsiService } from 'app/services/jitsi.service.js';
import { UserService, UserSchema } from 'app/services/user.service.js';
import swal from 'sweetalert2';
declare var JitsiMeetExternalAPI: any;
declare const $: any;
#Component({
selector: "app-conference-student-cmp",
templateUrl: "conference-student.component.html"
})
export class ConferenceStudentComponent implements OnInit {
roomName: string;
tutor: UserSchema
student: UserSchema;
domain: string;
options: any;
api: any;
hasActiveRoom: boolean;
tutorId: string;
conferenceJoined: boolean;
constructor(
private route: ActivatedRoute,
private jitsiService: JitsiService,
private userService: UserService
) { }
ngOnInit() {
this.conferenceJoined = false;
// this.domain = "jitsi.liquidclouds.in";
this.domain = 'meet.jit.si';
this.route.paramMap.subscribe((params: ParamMap) => this.tutorId = params.get('id'));
this.userService
.getLoggedUserDetail()
.subscribe(student => {
// store student
this.student = student;
this.userService.getUser(this.tutorId)
.subscribe((tutor: UserSchema) => {
// store tutor
this.tutor = tutor;
const obj = { tutorId: this.tutor['_id'], studentId: this.student['_id'] };
this.jitsiService.getActiveRoomForStudent(obj).subscribe(resp => {
if (resp && resp['result'] && resp['result']['roomName']) {
this.hasActiveRoom = true;
this.roomName = resp['result']['roomName'];
}
});
});
});
}
joinSession() {
this.options = {
roomName: this.roomName,
width: 800,
height: 500,
parentNode: document.querySelector('#jitsiVideo'),
configOverwrite: {},
interfaceConfigOverwrite: {
// filmStripOnly: true,
TOOLBAR_BUTTONS: [
'microphone', 'camera', 'closedcaptions', 'desktop', 'fullscreen',
'hangup', 'profile', 'chat', 'recording'
],
}
};
this.api = new JitsiMeetExternalAPI(this.domain, this.options);
this.api.executeCommand('displayName', this.student['fullName']);
this.api.addEventListeners({
readyToClose: this.unload,
participantLeft: this.handleParticipantLeft,
participantJoined: this.handleParticipantJoined,
videoConferenceJoined: this.handleVideoConferenceJoined,
videoConferenceLeft: this.handleVideoConferenceLeft
});
this.conferenceJoined = true;
}
unload = () => {
if (this.api instanceof JitsiMeetExternalAPI) {
this.api.dispose();
}
$('#jitsiVideo').html('');
this.conferenceJoined = false;
this.hasActiveRoom = false;
}
handleParticipantLeft = (arg) => {
this.jitsiService.getJitsiDetailByParticipantId(arg.id)
.subscribe(async roomDetail => {
if (!roomDetail) {
console.log(`No room is joined by the participant id: ${arg.id}`);
return false;
} else {
const participantDetail = roomDetail['participants'].filter(el => el.jitsiParticipantId === arg.id)[0];
if (participantDetail) {
switch (participantDetail.type) {
case 'manager':
console.log('Left participant is manager');
break;
case 'supervisor':
console.log('Left participant is supervisor');
break;
case 'student':
console.log('Left participant is student');
break;
case 'tutor':
console.log('Left participant is tutor');
this.api.dispose();
this.conferenceJoined = false;
this.hasActiveRoom = false;
alert('Tutor left the session.');
break;
default:
console.log('Left participant is not a valid type');
break;
}
}
}
});
}
handleParticipantJoined = (arg) => {
console.log('participant joined: ', arg, this.api);
}
handleVideoConferenceJoined = (arg) => {
const obj = {
participantId: arg.id,
roomName: arg.roomName,
tutorId: this.tutor['_id'],
studentId: this.student['_id'],
studentJoined: 'yes',
joineeType: 'student',
}
// save new room
this.jitsiService.updateJitsiRoomForStudent(obj);
}
handleVideoConferenceLeft = (arg) => {
}
}
admin-layout.componet.html
<div class="wrapper">
<div class="sidebar" data-color="rose" data-background-color="white" data-image="./assets/img/sidebar-1.jpg">
<app-sidebar-cmp></app-sidebar-cmp>
<div class="sidebar-background" style="background-image: url(assets/img/sidebar-1.jpg)"></div>
</div>
<div class="main-panel">
<router-outlet></router-outlet>
<div *ngIf="!isMap()">
<app-footer-cmp></app-footer-cmp>
</div>
</div>
<app-fixedplugin></app-fixedplugin>
</div>

Navigation in react-native

This's the homepage
What I want is when i click on the clock icon at the BottomTabNavigator, i have a new page with the features below:
The Tab navigator will be hidden
A new header
possibility to go back (go to the homepage)
I have already fixed the two first point... The third one makes me confused!
Is there someone who can help me ?
"dependencies": {
"expo": "^31.0.2",
"react": "16.5.0",
"react-elements": "^1.3.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-31.0.0.tar.gz",
"react-native-elements": "^0.19.1",
"react-native-snap-carousel": "^3.7.5",
"react-native-vector-icons": "^6.1.0",
"react-navigation": "^3.0.0"
},
**CODE : **
//Differents Stack Navigators
const AppStackNavigator = createAppContainer(createStackNavigator({
Home: {
screen: HomeScreen,
navigationOptions: {
header: < Head / >
}
},
Search: {
screen: Search,
navigationOptions: {
title: "Rechercher",
headerStyle: {
backgroundColor: '#00aced'
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
}
}
}
}));
const HoraireStackNAvigator = createAppContainer(createStackNavigator({
Horaire: {
screen: Horaires,
navigationOptions: {
title: "Horaires"
}
}
}))
const PaimentStackNAvigator = createAppContainer(createStackNavigator({
Horaire: {
screen: Paiement
}
}))
//The Principle TabNavigator
const TabContainer = createBottomTabNavigator({
Home: {
screen: AppStackNavigator,
},
Paiement: {
screen: PaimentStackNAvigator,
},
Horaires: {
screen: HoraireStackNAvigator,
navigationOptions: {
tabBarVisible: false
}
}
}, {
initialRouteName: 'Home',
order: ['Paiement', 'Horaires', 'Home', 'Proximite', 'Settings'],
//Default Options for the bottom Tab
defaultNavigationOptions: ({
navigation
}) => ({
tabBarIcon: ({
focused,
horizontal,
tintColor
}) => {
const {
routeName
} = navigation.state;
let iconName;
if (routeName === 'Home') {
iconName = `ios-home${focused ? '' : ''}`;
} else if (routeName === 'Settings') {
iconName = `ios-settings${focused ? '' : ''}`;
} else if (routeName === 'Horaires') {
iconName = `ios-clock${focused ? '' : ''}`;
} else if (routeName === 'Proximite') {
iconName = `ios-locate${focused ? '' : ''}`;
} else if (routeName === 'Paiement') {
iconName = `ios-cart${focused ? '' : ''}`;
}
return <Ionicons name = {
iconName
}
size = {
horizontal ? 20 : 35
}
color = {
tintColor
}
/>;
}
}),
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
tabStyle: {
backgroundColor: '#000'
},
showLabel: false,
showIcon: true
}
})
export default AppTabNavigator = createAppContainer(TabContainer);
React navigation stack provide default go back option its usually based on your stack.
close active screen and move back in the stack using
this.props.navigate("goBack")
this.props.navigate.pop("HomeScreen")
We can also push or navigate to HomeScreen by using
this.props.navigate.navigate("HomeScreen")
this.props.navigate.push("HomeScreen")

Opening SideMenu on button press

I'm currently trying to upgrade to react-native-navigation V2 from V1 and got stuck trying to find a way to toggle side menus on top bar button press.
My app starts with
Navigation.setRoot({
root: {
sideMenu: {
left: {
component: {
name: 'testApp.SideDrawer',
passProps: {
text: 'This is a left side menu screen'
}
}
},
center: {
bottomTabs: {
...
}
},
},
},
});
Is there a way to do it in current version?
Turned out you can't use this.props.navigator.toggleDrawer in V2 and should use Navigator.mergeOptions instead to change drawer visibility.
In my case:
1) First assign an Id to the drawer (id: leftSideDrawer)
Navigation.setRoot({
root: {
sideMenu: {
left: {
component: {
name: 'testApp.SideDrawer',
id: 'leftSideDrawer'
}
},
center: {
bottomTabs: {
...
}
},
},
},
});
2) Use it in to change drawer visibility
Navigation.mergeOptions('leftSideDrawer', {
sideMenu: {
left: {
visible: true
}
}
});
You can set a boolean in your component to identify the current state of the side drawer screen and then you can use that boolean to set the visibility of the drawer with mergeOptions. Basically toggle! Below is the snippet to achieve this.
constructor(props) {
super(props);
this.isSideDrawerVisible = false;
Navigation.events().bindComponent(this);
}
navigationButtonPressed({ buttonId }) {
if (buttonId === "openSideDrawer") {
(!this.isSideDrawerVisible) ? this.isSideDrawerVisible = true : this.isSideDrawerVisible = false
Navigation.mergeOptions(this.props.componentId, {
sideMenu: {
left: {
visible: this.isSideDrawerVisible,
}
}
});
}
}

pimcore workflow management - disabling Save and publish

In pimcore I tried implementing the workflow management for objects. Workflow is working fine. But, save and publish button are still appearing, how can I remove these features if workflow is enabled. 1. Disabling Save, publish, unpublish and delete to the user if workflow is enabled. 2. Removing same options (save, publish, unpublish and delete) on right clicking the object.
If you want to disable buttons you have to overwrite pimcore object.js(pimcore/static6/js/pimcore/object/object.js) and tree.js(pimcore/static6/js/pimcore/object/tree.js).
First create plugin. Then create object.js in static(Remember to add js paths to plugin.xml) and add code:
pimcore.registerNS("pimcore.object.object");
pimcore.object.object = Class.create(pimcore.object.object, {
getLayoutToolbar : function () {
if (!this.toolbar) {
var buttons = [];
this.toolbarButtons = {};
this.toolbarButtons.save = new Ext.SplitButton({
text: t('save'),
iconCls: "pimcore_icon_save",
scale: "medium",
handler: this.save.bind(this, "unpublish"),
menu:[{
text: t('save_close'),
iconCls: "pimcore_icon_save",
handler: this.unpublishClose.bind(this)
}]
});
this.toolbarButtons.publish = new Ext.SplitButton({
text: t('save_and_publish'),
iconCls: "pimcore_icon_publish",
scale: "medium",
handler: this.publish.bind(this),
menu: [{
text: t('save_pubish_close'),
iconCls: "pimcore_icon_save",
handler: this.publishClose.bind(this)
},
{
text: t('save_only_new_version'),
iconCls: "pimcore_icon_save",
handler: this.save.bind(this, "version")
},
{
text: t('save_only_scheduled_tasks'),
iconCls: "pimcore_icon_save",
handler: this.save.bind(this, "scheduler","scheduler")
}
]
});
this.toolbarButtons.unpublish = new Ext.Button({
text: t('unpublish'),
iconCls: "pimcore_icon_unpublish",
scale: "medium",
handler: this.unpublish.bind(this)
});
this.toolbarButtons.remove = new Ext.Button({
tooltip: t("delete"),
iconCls: "pimcore_icon_delete",
scale: "medium",
handler: this.remove.bind(this)
});
this.toolbarButtons.rename = new Ext.Button({
tooltip: t('rename'),
iconCls: "pimcore_icon_key pimcore_icon_overlay_go",
scale: "medium",
handler: function () {
var options = {
elementType: "object",
elementSubType: this.data.general.o_type,
id: this.id,
default: this.data.general.o_key
};
pimcore.elementservice.editElementKey(options);
}.bind(this)
});
//This code is for save&publish buttons
if (this.isAllowed("save")) {
buttons.push(this.toolbarButtons.save);
}
if (this.isAllowed("publish")) {
buttons.push(this.toolbarButtons.publish);
}
if (this.isAllowed("unpublish") && !this.data.general.o_locked) {
buttons.push(this.toolbarButtons.unpublish);
}
buttons.push("-");
if(this.isAllowed("delete") && !this.data.general.o_locked) {
buttons.push(this.toolbarButtons.remove);
}
if(this.isAllowed("rename") && !this.data.general.o_locked) {
buttons.push(this.toolbarButtons.rename);
}
var reloadConfig = {
xtype: "splitbutton",
tooltip: t('reload'),
iconCls: "pimcore_icon_reload",
scale: "medium",
handler: this.reload.bind(this, this.data.currentLayoutId)
};
if (this.data["validLayouts"] && this.data.validLayouts.length > 1) {
var menu = [];
for (var i = 0; i < this.data.validLayouts.length; i++) {
var menuLabel = ts(this.data.validLayouts[i].name);
if (Number(this.data.currentLayoutId) == this.data.validLayouts[i].id) {
menuLabel = "<b>" + menuLabel + "</b>";
}
menu.push({
text: menuLabel,
iconCls: "pimcore_icon_reload",
handler: this.reload.bind(this, this.data.validLayouts[i].id)
});
}
reloadConfig.menu = menu;
}
buttons.push(reloadConfig);
if (pimcore.elementservice.showLocateInTreeButton("object")) {
if (this.data.general.o_type != "variant" || this.data.general.showVariants) {
buttons.push({
tooltip: t('show_in_tree'),
iconCls: "pimcore_icon_show_in_tree",
scale: "medium",
handler: this.selectInTree.bind(this, this.data.general.o_type)
});
}
}
buttons.push({
tooltip: t("show_metainfo"),
iconCls: "pimcore_icon_info",
scale: "medium",
handler: this.showMetaInfo.bind(this)
});
buttons.push("-");
buttons.push({
xtype: 'tbtext',
text: t("id") + " " + this.data.general.o_id,
scale: "medium"
});
buttons.push("-");
buttons.push({
xtype: 'tbtext',
text: ts(this.data.general.o_className),
scale: "medium"
});
// version notification
this.newerVersionNotification = new Ext.Toolbar.TextItem({
xtype: 'tbtext',
text: ' <img src="/pimcore/static6/img/flat-color-icons/medium_priority.svg" style="height: 16px;" align="absbottom" /> '
+ t("this_is_a_newer_not_published_version"),
scale: "medium",
hidden: true
});
buttons.push(this.newerVersionNotification);
//workflow management
pimcore.elementservice.integrateWorkflowManagement('object', this.id, this, buttons);
// check for newer version than the published
if (this.data.versions.length > 0) {
if (this.data.general.o_modificationDate < this.data.versions[0].date) {
this.newerVersionNotification.show();
}
}
this.toolbar = new Ext.Toolbar({
id: "object_toolbar_" + this.id,
region: "north",
border: false,
cls: "main-toolbar",
items: buttons,
overflowHandler: 'scroller'
});
this.toolbar.on("afterrender", function () {
window.setTimeout(function () {
if (!this.data.general.o_published) {
this.toolbarButtons.unpublish.hide();
} else if (this.isAllowed("publish")) {
this.toolbarButtons.save.hide();
}
}.bind(this), 500);
}.bind(this));
}
return this.toolbar;
}
});
You have to do the same with tree.js:
pimcore.object.tree = Class.create({
onTreeNodeContextmenu: function (tree, record, item, index, e, eOpts ) {
e.stopEvent();
tree.select();
var menu = new Ext.menu.Menu();
var perspectiveCfg = this.perspectiveCfg;
var object_types = pimcore.globalmanager.get("object_types_store_create");
var objectMenu = {
objects: [],
importer: [],
ref: this
};
var groups = {
importer: {},
objects: {}
};
var tmpMenuEntry;
var tmpMenuEntryImport;
var $this = this;
object_types.each(function (classRecord) {
if ($this.config.allowedClasses && !in_array(classRecord.get("id"), $this.config.allowedClasses)) {
return;
}
tmpMenuEntry = {
text: classRecord.get("translatedText"),
iconCls: "pimcore_icon_object pimcore_icon_overlay_add",
handler: $this.addObject.bind($this, classRecord.get("id"), classRecord.get("text"), tree, record)
};
// add special icon
if (classRecord.get("icon") != "/pimcore/static6/img/flat-color-icons/timeline.svg") {
tmpMenuEntry.icon = classRecord.get("icon");
tmpMenuEntry.iconCls = "";
}
tmpMenuEntryImport = {
text: classRecord.get("translatedText"),
iconCls: "pimcore_icon_object pimcore_icon_overlay_add",
handler: $this.importObjects.bind($this, classRecord.get("id"), classRecord.get("text"), tree, record)
};
// add special icon
if (classRecord.get("icon") != "/pimcore/static6/img/flat-color-icons/timeline.svg") {
tmpMenuEntryImport.icon = classRecord.get("icon");
tmpMenuEntryImport.iconCls = "";
}
// check if the class is within a group
if(classRecord.get("group")) {
if(!groups["objects"][classRecord.get("group")]) {
groups["objects"][classRecord.get("group")] = {
text: classRecord.get("group"),
iconCls: "pimcore_icon_folder",
hideOnClick: false,
menu: {
items: []
}
};
groups["importer"][classRecord.get("group")] = {
text: classRecord.get("group"),
iconCls: "pimcore_icon_folder",
hideOnClick: false,
menu: {
items: []
}
};
objectMenu["objects"].push(groups["objects"][classRecord.get("group")]);
objectMenu["importer"].push(groups["importer"][classRecord.get("group")]);
}
groups["objects"][classRecord.get("group")]["menu"]["items"].push(tmpMenuEntry);
groups["importer"][classRecord.get("group")]["menu"]["items"].push(tmpMenuEntryImport);
} else {
objectMenu["objects"].push(tmpMenuEntry);
objectMenu["importer"].push(tmpMenuEntryImport);
}
});
var isVariant = record.data.type == "variant";
if (record.data.permissions.create) {
if (!isVariant) {
if (perspectiveCfg.inTreeContextMenu("object.add")) {
menu.add(new Ext.menu.Item({
text: t('add_object'),
iconCls: "pimcore_icon_object pimcore_icon_overlay_add",
hideOnClick: false,
menu: objectMenu.objects
}));
}
}
if (record.data.allowVariants && perspectiveCfg.inTreeContextMenu("object.add")) {
menu.add(new Ext.menu.Item({
text: t("add_variant"),
iconCls: "pimcore_icon_variant",
handler: this.createVariant.bind(this, tree, record)
}));
}
if (!isVariant) {
if (perspectiveCfg.inTreeContextMenu("object.addFolder")) {
menu.add(new Ext.menu.Item({
text: t('add_folder'),
iconCls: "pimcore_icon_folder pimcore_icon_overlay_add",
handler: this.addFolder.bind(this, tree, record)
}));
}
if (perspectiveCfg.inTreeContextMenu("object.importCsv")) {
menu.add({
text: t('import_csv'),
hideOnClick: false,
iconCls: "pimcore_icon_object pimcore_icon_overlay_upload",
menu: objectMenu.importer
});
}
menu.add("-");
//paste
var pasteMenu = [];
if (perspectiveCfg.inTreeContextMenu("object.paste")) {
if (pimcore.cachedObjectId && record.data.permissions.create) {
pasteMenu.push({
text: t("paste_recursive_as_childs"),
iconCls: "pimcore_icon_paste",
handler: this.pasteInfo.bind(this, tree, record, "recursive")
});
pasteMenu.push({
text: t("paste_recursive_updating_references"),
iconCls: "pimcore_icon_paste",
handler: this.pasteInfo.bind(this, tree, record, "recursive-update-references")
});
pasteMenu.push({
text: t("paste_as_child"),
iconCls: "pimcore_icon_paste",
handler: this.pasteInfo.bind(this, tree, record, "child")
});
if (record.data.type != "folder") {
pasteMenu.push({
text: t("paste_contents"),
iconCls: "pimcore_icon_paste",
handler: this.pasteInfo.bind(this, tree, record, "replace")
});
}
}
}
if (!isVariant) {
if (pimcore.cutObject && record.data.permissions.create) {
pasteMenu.push({
text: t("paste_cut_element"),
iconCls: "pimcore_icon_paste",
handler: function () {
this.pasteCutObject(pimcore.cutObject,
pimcore.cutObjectParentNode, record, this.tree);
pimcore.cutObjectParentNode = null;
pimcore.cutObject = null;
}.bind(this)
});
}
if (pasteMenu.length > 0) {
menu.add(new Ext.menu.Item({
text: t('paste'),
iconCls: "pimcore_icon_paste",
hideOnClick: false,
menu: pasteMenu
}));
}
}
}
}
if (!isVariant) {
if (record.data.id != 1 && record.data.permissions.view && perspectiveCfg.inTreeContextMenu("object.copy")) {
menu.add(new Ext.menu.Item({
text: t('copy'),
iconCls: "pimcore_icon_copy",
handler: this.copy.bind(this, tree, record)
}));
}
//cut
if (record.data.id != 1 && !record.data.locked && record.data.permissions.rename && perspectiveCfg.inTreeContextMenu("object.cut")) {
menu.add(new Ext.menu.Item({
text: t('cut'),
iconCls: "pimcore_icon_cut",
handler: this.cut.bind(this, tree, record)
}));
}
}
//publish
if (record.data.type != "folder" && !record.data.locked) {
if (record.data.published && record.data.permissions.unpublish && perspectiveCfg.inTreeContextMenu("object.unpublish")) {
menu.add(new Ext.menu.Item({
text: t('unpublish'),
iconCls: "pimcore_icon_unpublish",
handler: this.publishObject.bind(this, tree, record, 'unpublish')
}));
} else if (!record.data.published && record.data.permissions.publish && perspectiveCfg.inTreeContextMenu("object.publish")) {
menu.add(new Ext.menu.Item({
text: t('publish'),
iconCls: "pimcore_icon_publish",
handler: this.publishObject.bind(this, tree, record, 'publish')
}));
}
}
if (record.data.permissions["delete"] && record.data.id != 1 && !record.data.locked && perspectiveCfg.inTreeContextMenu("object.delete")) {
menu.add(new Ext.menu.Item({
text: t('delete'),
iconCls: "pimcore_icon_delete",
handler: this.remove.bind(this, tree, record)
}));
}
if (record.data.permissions.rename && record.data.id != 1 && !record.data.locked && perspectiveCfg.inTreeContextMenu("object.rename")) {
menu.add(new Ext.menu.Item({
text: t('rename'),
iconCls: "pimcore_icon_key pimcore_icon_overlay_go",
handler: this.editObjectKey.bind(this, tree, record)
}));
}
// advanced menu
var advancedMenuItems = [];
var user = pimcore.globalmanager.get("user");
if (record.data.permissions.create && perspectiveCfg.inTreeContextMenu("object.searchAndMove")) {
advancedMenuItems.push({
text: t('search_and_move'),
iconCls: "pimcore_icon_search pimcore_icon_overlay_go",
handler: this.searchAndMove.bind(this, tree, record)
});
}
if (record.data.id != 1 && user.admin) {
var lockMenu = [];
if (record.data.lockOwner && perspectiveCfg.inTreeContextMenu("object.unlock")) { // add unlock
lockMenu.push({
text: t('unlock'),
iconCls: "pimcore_icon_lock pimcore_icon_overlay_delete",
handler: function () {
pimcore.elementservice.lockElement({
elementType: "object",
id: record.data.id,
mode: "null"
});
}.bind(this)
});
} else {
if (perspectiveCfg.inTreeContextMenu("object.lock")) {
lockMenu.push({
text: t('lock'),
iconCls: "pimcore_icon_lock pimcore_icon_overlay_add",
handler: function () {
pimcore.elementservice.lockElement({
elementType: "object",
id: record.data.id,
mode: "self"
});
}.bind(this)
});
}
if (perspectiveCfg.inTreeContextMenu("object.lockAndPropagate")) {
lockMenu.push({
text: t('lock_and_propagate_to_childs'),
iconCls: "pimcore_icon_lock pimcore_icon_overlay_go",
handler: function () {
pimcore.elementservice.lockElement({
elementType: "object",
id: record.data.id,
mode: "propagate"
});
}.bind(this)
});
}
}
if(record.data.locked) {
// add unlock and propagate to children functionality
if (perspectiveCfg.inTreeContextMenu("object.unlockAndPropagate")) {
lockMenu.push({
text: t('unlock_and_propagate_to_children'),
iconCls: "pimcore_icon_lock pimcore_icon_overlay_delete",
handler: function () {
pimcore.elementservice.unlockElement({
elementType: "object",
id: record.data.id
});
}.bind(this)
});
}
}
if (lockMenu.length > 0) {
advancedMenuItems.push({
text: t('lock'),
iconCls: "pimcore_icon_lock",
hideOnClick: false,
menu: lockMenu
});
}
}
menu.add("-");
if(advancedMenuItems.length) {
menu.add({
text: t('advanced'),
iconCls: "pimcore_icon_more",
hideOnClick: false,
menu: advancedMenuItems
});
}
if (perspectiveCfg.inTreeContextMenu("object.reload")) {
menu.add({
text: t('refresh'),
iconCls: "pimcore_icon_reload",
handler: this.reloadNode.bind(this, tree, record)
});
}
pimcore.helpers.hideRedundantSeparators(menu);
pimcore.plugin.broker.fireEvent("prepareObjectTreeContextMenu", menu, this, record);
menu.showAt(e.pageX+1, e.pageY+1);
},
});

Resources