Commit 153dd042 authored by Frank Bergmann's avatar Frank Bergmann

Initial Import

parents
Pipeline #636 failed with stages
<?xml version="1.0"?>
<!-- Generated by the OpenACS Package Manager -->
<package key="senchatouch-timesheet" url="http://openacs.org/repository/apm/packages/senchatouch-timesheet" type="apm_application">
<package-name>Sencha Touch Timesheet</package-name>
<pretty-plural>Sencha Touch Timesheet</pretty-plural>
<initial-install-p>f</initial-install-p>
<singleton-p>t</singleton-p>
<implements-subsite-p>f</implements-subsite-p>
<inherit-templates-p>f</inherit-templates-p>
<auto-mount>senchatouch-timesheet</auto-mount>
<version name="0.1d" url="http://openacs.org/repository/download/apm/senchatouch-timesheet-0.1d.apm">
<owner url="mailto:frank.bergmann@project-open.com">Frank Bergmann</owner>
<summary>Log hours using your mobile device</summary>
<vendor url="http://www.project-open.com/">]project-open[</vendor>
<description format="text/plain">Allows to log hours on a list of frequently used projects.</description>
<maturity>0</maturity>
<provides url="senchatouch-timesheet" version="0.1d"/>
<callbacks>
</callbacks>
<parameters>
<!-- No version parameters -->
</parameters>
</version>
</package>
Ext.application({
name: 'PO',
models: [
'Note',
'Project'
],
stores: [
'NoteStore',
'ProjectTimesheetStore'
],
views: [
'SplashPage',
'NoteDetail',
'NoteList',
'NoteNavigationView',
'ProjectList'
],
controllers: [
'NoteNavigationController'
],
viewport: {
autoMaximize: true
},
// Main function: Load the various panels
launch: function() {
Ext.create("Ext.tab.Panel", {
fullscreen: true,
tabBarPosition: 'bottom',
items: [
// The application consists of two pages only:
{xtype: 'projectList'},
{xtype: 'splashPage'},
{xtype: 'noteNavigationView'}
]
});
}
});
{
"name": "List",
"js": [
{
"path": "/senchatouch-v211/sencha-touch-all-debug.js"
},
{
"path": "app.js",
"update": "delta"
}
],
"css": [
{
"path": "/senchatouch-v211/resources/css/sencha-touch.css",
"update": "delta"
}
],
"appCache": {
"cache": [
"index.html"
],
"network": [
"*"
],
"fallback": []
},
"extras": [
"contacts.json",
"resources/icons",
"resources/loading"
],
"archivePath": "archive",
"buildPaths": {
"testing": "../../deploy/testing/list",
"production": "../../deploy/production/list",
"package": "../../deploy/package/list",
"native": "../../deploy/native/list"
},
"buildOptions": {
"product": "touch",
"minVersion": 3,
"debug": false,
"logger": "no"
},
"id": "3a867610-670a-11e1-a90e-4318029d18bb"
}
Ext.define('PO.controller.NoteNavigationController', {
extend: 'Ext.app.Controller',
xtype: 'noteNavigationController',
config: {
refs: {
noteNavigationView: 'noteNavigationView'
},
control: {
'noteNavigationView': {
initialize: 'onInitializeNavigationView'
},
'noteList': {
disclose: 'showDetail'
}
}
},
// Initialization of the Container - add a button
// The NavigationView itself doesn't seem to allow for this type of customization
onInitializeNavigationView: function(navView) {
var navBar = Ext.ComponentQuery.query('noteNavigationView')[0].getNavigationBar();
navBar.add({
xtype: 'button',
text: 'New Note',
align: 'right',
handler: function() {
console.log('NoteListController: New Note button pressed');
navView.push({
xtype: 'noteDetail',
title: 'New Note'
});
}
});
},
// "Disclose" Event - somebody pressed on the -> button at the list
// Create a new instance of the noteDetail page and push on the top
// of the stack
showDetail: function(list, record) {
var view = this.getNoteNavigationView();
view.push({
xtype: 'noteDetail',
record: record
});
}
});
Ext.define('PO.model.Note', {
extend: 'Ext.data.Model',
config: {
fields: [
'id',
'note',
'object_id',
'note_status_id',
'note_type_id'
],
proxy: {
type: 'rest',
url: '/intranet-rest/im_note',
appendId: true, // Append the object_id: ../im_ticket/<object_id>
timeout: 300000,
extraParams: {
format: 'json', // Tell the ]po[ REST to return JSON data.
deref_p: '1',
columns: 'note,note_type_id'
},
reader: {
type: 'json', // Tell the Proxy Reader to parse JSON
root: 'data', // Where do the data start in the JSON file?
totalProperty: 'total' // Total number of tickets for pagination
},
writer: {
type: 'json' // Allow Sencha to write ticket changes
}
}
}
});
Ext.define('PO.model.Project', {
extend: 'Ext.data.Model',
config: {
fields: [
'id',
'project_id', // The primary key or object_id of the project
'creation_user', // User_id of the guy creating the project
'project_name', // The name of the project
'project_nr', // The short name of the project.
'project_path', // The short name of the project.
'parent_id', // The parent of the project or NULL for a main project
'tree_sortkey', // A strange bitstring that determines the hierarchical position
'company_id', // Company for whom the project has been created
'project_status_id', // 76=open, 81=closed, ...
'project_type_id', // 100=Task, 101=Ticket, 2501=Consulting Project, ...
'start_date', // '2001-01-01'
'end_date',
'project_lead_id', // Project manager
'percent_completed', // 0 - 100: Defines what has already been done.
'on_track_status_id', // 66=green, 67=yellow, 68=red
'description',
'note',
'level', // 0 for a main project, 1 for a sub-project etc.
{ name: 'project_name_indented',
convert: function(value, record) {
var project_name = record.get('project_name');
var level = record.get('level');
while (level > 0) {
project_name = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + project_name;
level = level - 1;
}
return project_name;
}
}
],
proxy: {
type: 'rest',
url: '/intranet-rest/im_project',
appendId: true, // Append the object_id: ../im_ticket/<object_id>
timeout: 300000,
extraParams: {
format: 'json', // Tell the ]po[ REST to return JSON data.
deref_p: '1'
},
reader: {
type: 'json', // Tell the Proxy Reader to parse JSON
root: 'data', // Where do the data start in the JSON file?
totalProperty: 'total' // Total number of tickets for pagination
},
writer: {
type: 'json' // Allow Sencha to write ticket changes
}
}
}
});
Ext.define('PO.store.FilteredStore', {
extend: 'Ext.data.Store',
requires: ['Ext.data.Model'],
///////////////////////////////////////////////////////////////////////////
// Configuration
config: {
model: 'Ext.data.Model',
sourceStore: undefined,
filter: undefined
},
///////////////////////////////////////////////////////////////////////////
// Fields
sourceStore: undefined,
///////////////////////////////////////////////////////////////////////////
// Configuration methods
updateSourceStore: function (newValue, oldValue) {
// See if we've received a valid source store
if (!newValue)
return;
// Resolve the source store
this.sourceStore = Ext.data.StoreManager.lookup(newValue);
if (!this.sourceStore || !Ext.isObject(this.sourceStore) || !this.sourceStore.isStore)
Ext.Error.raise({ msg: 'An invalid source store (' + newValue + ') was provided for ' + this.self.getName() });
// Listen to source store events and copy model
this.setModel(this.sourceStore.getModel());
this.sourceStore.on({
addrecords: 'sourceStoreAdded',
removerecords: 'sourceStoreRemoved',
refresh: 'sourceStoreChanged',
scope: this
});
// Load the current data
this.sourceStoreChanged();
},
updateFilter: function () {
// Load the current data
this.sourceStoreChanged();
},
///////////////////////////////////////////////////////////////////////////
// Store overrides
fireEvent: function (eventName, me, record) {
// Intercept update events, remove rather than update if record is no longer valid
var filter = this.getFilter();
if (filter && eventName === 'updaterecord' && !filter(record))
this.remove(record);
else
this.callParent(arguments);
},
///////////////////////////////////////////////////////////////////////////
// Event handlers
sourceStoreAdded: function (sourceStore, records) {
var filter = this.getFilter();
if (!filter)
return;
// Determine which records belong in this store
var i = 0, len = records.length, record, newRecords = [];
for (; i < len; i++) {
record = records[i];
// Don't add records already in the store
if (this.indexOf(record) != -1)
continue;
if (filter(record))
newRecords.push(record);
}
// Add the new records
if (newRecords.length)
this.add(newRecords);
},
sourceStoreRemoved: function (sourceStore, records) {
this.remove(records);
},
sourceStoreChanged: function () {
// Clear the store
this.removeAll();
var records = [],
i, all, record,
filter = this.getFilter();
// No filter? No data
if (!filter)
return;
// Collect and filter the current records
all = this.sourceStore.getAll();
for (i = 0; i < all.length; i++) {
record = all[i];
if (filter(record))
records.push(record);
}
// Add the records to the store
this.add(records);
}
});
Ext.define('PO.store.NoteStore', {
extend: 'Ext.data.Store',
storeId: 'noteStore',
config: {
model: 'PO.model.Note',
autoLoad: true,
grouper: {
groupFn: function(record) {
var fn = record.get('note');
if (fn == null) { fn = 'a'; }
return fn.toLowerCase()[0];
}
},
sorters: [{
property: 'note',
direction: 'ASC'
}],
proxy: {
type: 'rest',
url: '/intranet-rest/im_note',
appendId: true,
extraParams: {
format: 'json'
},
reader: {
type: 'json',
rootProperty: 'data'
}
}
}
});
Ext.define('PO.store.NoteStore', {
extend: 'Ext.data.Store',
storeId: 'noteStore',
config: {
model: 'PO.model.Note',
autoLoad: true,
sorters: 'last_name',
grouper: {
groupFn: function(record) {
var fn = record.get('note');
if (fn == null) { fn = 'a'; }
return fn.toLowerCase()[0];
}
},
sorters: [{
property: 'note',
direction: 'ASC'
}],
proxy: {
type: 'rest',
url: '/intranet-rest/im_note',
appendId: true,
extraParams: {
format: 'json'
},
reader: {
type: 'json',
rootProperty: 'data'
}
}
}
});
Ext.define('PO.store.ProjectTimesheetStore', {
extend: 'Ext.data.Store',
storeId: 'projectTimesheetStore',
config: {
model: 'PO.model.Project',
autoLoad: true,
sorters: [{
/* Sort by tree_sortkey (hierarchical index)
* This way we automatically get a hierarchical list.
*/
property: 'tree_sortkey',
direction: 'ASC'
}],
grouper: {
/* Return the name of the tasks's most parent project.
* To get the main project we need to lookup the first 32 bit of the
* tasks's tree_sortkey (hierarchical index, calculated at the ]po[
* back-end.
*/
groupFn: function(record) {
var tree_sortkey = record.get('tree_sortkey').substr(0,32);
var main_project_record = Ext.getStore('ProjectTimesheetStore').getById(tree_sortkey);
if (main_project_record == null) { return ''; }
var main_project_name = main_project_record.get('project_name');
return main_project_name;
}
},
filters: [
// { property: 'parent_id', value: /a/ }
// { filterFn: function(item) { return item.get('parent_id') == ''; }}
],
proxy: {
type: 'rest',
url: '/intranet-reporting/view',
appendId: true,
extraParams: {
format: 'json',
report_code: 'rest_my_timesheet_projects'
},
reader: {
type: 'json',
rootProperty: 'data'
}
}
}
});
Ext.define('PO.view.NoteDetail', {
extend: 'Ext.form.Panel',
xtype: 'noteDetail',
config: {
title: 'Note Detail',
layout: 'vbox',
items: [{
xtype: 'fieldset',
title: 'Edit Note',
items: [{
xtype: 'selectfield',
name: 'note_type_id',
label: 'Type',
options: [
{text: 'Address', value: '11500'},
{text: 'Email', value: '11502'},
{text: 'Http', value: '11504'},
{text: 'Ftp', value: '11506'},
{text: 'Phone', value: '11508'},
{text: 'Fax', value: '11510'},
{text: 'Mobile', value: '11512'},
{text: 'Other', value: '11514'}
]
}, {
xtype: 'textareafield',
name: 'note',
label: 'Note'
}, {
xtype: 'hiddenfield',
name: 'id'
}, {
xtype: 'hiddenfield',
name: 'note_status_id',
value: 11400
}, {
xtype: 'hiddenfield',
name: 'object_id',
label: 'Object ID',
value: 0 // Magic value: 0 is the ID of the "guest" object
}
]
}, {
xtype: 'button',
text: 'Save',
ui: 'confirm',
handler: function() {
console.log('NoteDetail: Button "Save" pressed:');
// Save the form values to the record.
// The record was set by the NoteNavigationController
var form = this.up('formpanel');
var values = form.getValues();
var rec = form.getRecord();
// Did we create a completely new note?
if (typeof rec === "undefined" || rec == null) {
rec = Ext.ModelManager.create(values, 'PO.model.Note');
}
// Save the model - generates PUT or POST to REST backend
rec.set(values);
rec.save();
// reload the store
var noteStore = Ext.data.StoreManager.lookup('NoteStore');
noteStore.load();
// Return to the list of notes page
var navView = this.up('noteNavigationView');
navView.pop();
}
}
]
}
});
Ext.define('PO.view.NoteList', {
extend: 'Ext.List',
xtype: 'noteList',
requires: ['PO.store.NoteStore'],
config: {
title: 'Note List',
iconCls: 'star',
itemTpl: '<div class="contact2">{note}</div>',
disclosure: true,
grouped: true,
indexBar: true,
store: 'NoteStore',
onItemDisclosure: true
}
});
Ext.define('PO.view.NoteNavigationView', {
extend: 'Ext.navigation.View',
xtype: 'noteNavigationView',
requires: [
'PO.view.NoteList',
'PO.view.NoteDetail'
],
config: {
title: 'Notes',
iconCls: 'star',
items: [{
xtype: 'noteList'
}]
}
});
Ext.define('PO.view.ProjectList', {
extend: 'Ext.List',
xtype: 'projectList',
requires: ['PO.store.ProjectTimesheetStore'],
config: {
title: 'Project List',
iconCls: 'star',
itemTpl: '<div class="contact2">{project_name_indented}</div>',
disclosure: true,
grouped: true,
indexBar: true,
store: 'ProjectTimesheetStore',
onItemDisclosure: true
}
});
Ext.define('PO.view.SplashPage', {
extend: 'Ext.Panel',
xtype: 'splashPage',
config: {
title: 'Home',
iconCls: 'home',
scrollable: 'vertical',
// styleHtmlContent: true,
html: [
'<br>&nbsp;<br>&nbsp;<br>',
'<center>',
'<img src="/senchatouch-notes/resources/startup/project_open.250x91.gif"/>',
'<br>&nbsp;<br>',
'<h1>]po[ Sencha Touch Notes</h1>',
'<br>&nbsp;<br>',
'<p>This demo shows how to list, update and create notes objects using a ]po[ backend.',
'</center>',
].join("")
}
});
<!DOCTYPE HTML>
<html manifest="" lang="en-US">
<head>
<meta charset="UTF-8">
<title>]po[ Timesheet</title>
<script id="microloader" type="text/javascript" src="/senchatouch-v211/microloader/development.js"></script>
</head>
<body>
</body>
</html>
# /packages/senchatouch-notes/www/index.tcl
#
# Copyright (c) 2003-2013 ]project-open[
# All rights reserved
ad_page_contract {
@author frank.bergmann@ticket-open.com
} {
}
# ---------------------------------------------------------------
# Defaults & Security
# ---------------------------------------------------------------
set current_user_id [ad_maybe_redirect_for_registration]
.extjs-indent-level-0 {
font-size: 11px; padding-left: 5px
}
.extjs-indent-level-1 {
font-size: 11px; padding-left: 25px
}
.extjs-indent-level-2 {
font-size: 11px; padding-left: 45px
}
.extjs-indent-level-3 {
font-size: 11px; padding-left: 65px
}
.extjs-indent-level-4 {
font-size: 11px; padding-left: 85px
}
.extjs-indent-level-5 {
font-size: 11px; padding-left: 105px
}
.extjs-indent-level-6 {
font-size: 11px; padding-left: 125px
}
.extjs-indent-level-7 {
font-size: 11px; padding-left: 145px
}
.extjs-indent-level-8 {
font-size: 11px; padding-left: 165px
}
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