I has this simply code
import qbs
Project {
name: "simple_test"
Product {
name: "micro"
type: "other"
Group {
files: '*.q'
fileTags: ['qfile']
}
Rule {
id: check1
inputs: ["qfile"]
prepare: {
var cmd = new JavaScriptCommand();
cmd.description = "QFile passing"
cmd.silent = false;
cmd.highlight = "compiler";
cmd.sourceCode = function() {
print("Nothing to do");
};
return cmd;
}
}
Transformer {
inputs: ['blink.q']
Artifact {
filePath: "processed_qfile.txt"
fileTags: "processed_qfile"
}
prepare: {
var cmd = new JavaScriptCommand();
cmd.description = "QFile transformer";
cmd.highlight = "compiler";
cmd.sourceCode = function() {
print("Another nothing");
};
return cmd;
}
}
}
}
And put two files blink.q and blink1.q
By documentation, I must see in "compile output" windows 3 lines: Two with
"QFile Passing" and one with "QFile transformer"
But I see that only Transformer block is work (no "QFile Passing" at all) ;( What is wrong with my Rule?
Your Rule must actually generate some Artifact(s), and the type of your product must somehow (directly or indirectly) depend on the file tags of the output artifacts of your Rule. In other words, nothing depended on the output of your Rule, so the Rule was not executed.
Probably what you want is the following:
import qbs
Project {
name: "simple_test"
Product {
name: "micro"
type: ["other", "processed_qfile"]
Group {
files: '*.q'
fileTags: ['qfile']
}
Rule {
id: check1
inputs: ["qfile"]
Artifact {
filePath: "processed_qfile.txt"
fileTags: "processed_qfile"
}
prepare: {
var cmd = new JavaScriptCommand();
cmd.description = "QFile passing"
cmd.silent = false;
cmd.highlight = "compiler";
cmd.sourceCode = function() {
print("Nothing to do");
};
return cmd;
}
}
}
}
Note the addition of:
An Artifact item inside the check1 Rule describing the output file that will be produced by the Rule.
The addition of the processed_qfile to the Product's type, creating a connection in the dependency tree and causing the Rule to be executed when the product is built
Related
I have alfresco share/repo version 5.2.3
I'm trying to modifiy the function found in _AlfDndDocumentUploadMixin.js with the fix that has been posted on https://github.com/Alfresco/Aikau/blob/1.0.101_hotfixes/aikau/src/main/resources/alfresco/documentlibrary/_AlfDndDocumentUploadMixin.js
I tried extending the attachment-doclib.xml and get the jsonmodul, but it gives me a null pointer.
My code is the following
From attachment-doclib.get.js
var documentServices = model.jsonModel;
for (var i=0; i<documentServices.length; i++)
{
if (documentServices[i] === "alfresco/documentlibrary")
{
documentServices[i] = "js/aikau/1.0.101.10/alfresco/documentlibrary/my-documentlibrary/_AlfDndDocumentUploadMixin-extension";
}
else if (documentServices[i].name === "alfresco/documentlibrary")
{
documentServices[i].name = "js/aikau/1.0.101.10/alfresco/documentlibrary/my-documentlibrary/_AlfDndDocumentUploadMixin-extension";
}
}
from doclib-customizations.xml
<config evaluator="string-compare" condition="WebFramework" replace="false">
<web-framework>
<dojo-pages>
<packages>
<package name="documentlibrary" location="js/aikau/1.0.101.10/alfresco/documentlibrary" />
</packages>
</dojo-pages>
</web-framework>
</config>
From _alfDndDocumentUploadMixin-extended.js
onDndUploadDrop: function alfresco_documentlibrary__AlfDndDocumentUploadMixin__onDndUploadDrop(evt) {
try
{
// Only perform a file upload if the user has *actually* dropped some files!
this.alfLog("log", "Upload drop detected", evt);
if (evt.dataTransfer.files !== undefined && evt.dataTransfer.files !== null && evt.dataTransfer.files.length > 0)
{
this.removeDndHighlight();
var destination = this._currentNode ? this._currentNode.nodeRef : null;
var config = this.getUploadConfig();
var defaultConfig = {
destination: destination,
siteId: null,
containerId: null,
uploadDirectory: null,
updateNodeRef: null,
description: "",
overwrite: false,
thumbnails: "doclib",
username: null
};
var updatedConfig = lang.mixin(defaultConfig, config);
var walkFileSystem = lang.hitch(this, function alfresco_documentlibrary__AlfDndDocumentUploadMixin__onDndUploadDrop__walkFileSystem(directory, callback, error) {
callback.limit = this.dndMaxFileLimit;
callback.pending = callback.pending || 0;
callback.files = callback.files || [];
// get a dir reader and cleanup file path
var reader = directory.createReader(),
relativePath = directory.fullPath.replace(/^\//, "");
var repeatReader = function alfresco_documentlibrary__AlfDndDocumentUploadMixin__onDndUploadDrop__walkFileSystem__repeatReader() {
// about to start an async callback function
callback.pending++;
reader.readEntries(function alfresco_documentlibrary__AlfDndDocumentUploadMixin__onDndUploadDrop__walkFileSystem__repeatReader__readEntries(entries) {
// processing an async callback function
callback.pending--;
array.forEach(entries, function(entry) {
if (entry.isFile)
{
// about to start an async callback function
callback.pending++;
entry.file(function(File) {
// add the relativePath property to each file - this can be used to rebuild the contents of
// a nested tree folder structure if an appropriate API is available to do so
File.relativePath = relativePath;
callback.files.push(File);
if (callback.limit && callback.files.length > callback.limit)
{
throw new Error("Maximum dnd file limit reached: " + callback.limit);
}
// processing an async callback function
if (--callback.pending === 0)
{
// fall out here if last item processed is a file entry
callback(callback.files);
}
}, error);
}
else
{
walkFileSystem(entry, callback, error);
}
});
// the reader API is a little esoteric,from the MDN docs:
// "Continue calling readEntries() until an empty array is returned.
// You have to do this because the API might not return all entries in a single call."
if (entries.length !== 0)
{
repeatReader();
}
// fall out here if last item processed is a dir entry e.g. empty dir
if (callback.pending === 0)
{
callback(callback.files);
}
}, error);
};
repeatReader();
});
var addSelectedFiles = lang.hitch(this, function alfresco_documentlibrary__AlfDndDocumentUploadMixin__onDndUploadDrop__addSelectedFiles(files) {
if (this.dndMaxFileLimit && files.length > this.dndMaxFileLimit)
{
throw new Error("Maximum dnd file limit reached: " + this.dndMaxFileLimit);
}
// Check to see whether or not the generated upload configuration indicates
// that an existing node will be created or not. If node is being updated then
// we need to generate an intermediary step to capture version and comments...
if (updatedConfig.overwrite === false)
{
// Set up a response topic for receiving notifications that the upload has completed...
var responseTopic = this.generateUuid();
this._uploadSubHandle = this.alfSubscribe(responseTopic, lang.hitch(this, this.onFileUploadComplete), true);
this.alfPublish(topics.UPLOAD_REQUEST, {
alfResponseTopic: responseTopic,
files: files,
targetData: updatedConfig
}, true);
}
else
{
// TODO: Check that only one file has been dropped and issue error...
this.publishUpdateRequest(updatedConfig, files);
}
});
var items = evt.dataTransfer.items || [], firstEntry;
// webkitGetAsEntry is a marker for determining FileSystem API support.
// SHA-2164 - Firefox claims support, but different impl. rather than code around differences, fallback.
if (items[0] && items[0].webkitGetAsEntry && !has("ff") && (firstEntry = items[0].webkitGetAsEntry()))
{
walkFileSystem(firstEntry.filesystem.root, function(files) {
addSelectedFiles(files);
}, function() {
// fallback to standard way if error happens
addSelectedFiles(evt.dataTransfer.files);
}
);
}
else
{
// fallback to standard way if no support for filesystem API
addSelectedFiles(evt.dataTransfer.files);
}
}
else
{
this.alfLog("error", "A drop event was detected, but no files were present for upload: ", evt.dataTransfer);
}
}
catch(exception)
{
this.alfLog("error", "The following error occurred when files were dropped onto the Document List: ", exception);
}
// Remove the drag highlight...
this.removeDndHighlight();
// Destroy the overlay node (required for views that will re-render all the contents)...
domConstruct.destroy(this.dragAndDropOverlayNode);
this.dragAndDropOverlayNode = null;
evt.stopPropagation();
evt.preventDefault();
},
/**
* This function publishes an update version request. It will request that a new dialog
* be displayed containing the form controls defined in
* [widgetsForUpdate]{#link module:alfresco/documentlibrary/_AlfDndDocumentUploadMixin#widgetsForUpdate}.
*
* #instance
* #param {object} uploadConfig
*
* #fires ALF_CREATE_FORM_DIALOG_REQUEST
*/
publishUpdateRequest: function alfresco_documentlibrary__AlfDndDocumentUploadMixin__publishUpdateRequest(uploadConfig, files) {
// TODO: Work out the next minor and major increment versions...
// TODO: Localization required...
// Set up a response topic for receiving notifications that the upload has completed...
var responseTopic = this.generateUuid();
this._uploadSubHandle = this.alfSubscribe(responseTopic, lang.hitch(this, this.onFileUploadComplete), true);
// To avoid the issue with processing payloads containing files with native
// code in them, it is necessary to temporarily store the files in the data model...
var filesRef = this.generateUuid();
this.alfSetData(filesRef, files);
this.alfPublish("ALF_CREATE_FORM_DIALOG_REQUEST", {
dialogTitle: "Update",
dialogConfirmationButtonTitle: "Continue Update",
dialogCancellationButtonTitle: "Cancel",
formSubmissionTopic: topics.UPLOAD_REQUEST,
formSubmissionPayloadMixin: {
alfResponseTopic: responseTopic,
filesRefs: filesRef,
targetData: uploadConfig
},
fixedWidth: true,
widgets: lang.clone(this.widgetsForUpdate)
}, true);
},
Expect: All files that are dragged and drop to be uploaded.
Actual: Only one file is uploaded
I've created an UI action using various guides (include Jeff Potts really great ones) successfully and it function exactly as expected - but I want to add that action to the multi-select tool as well. It has been really difficult finding much documentation.
Some things I've tried:
Tried to find out if there was an applicable actionGroup - which there doesn't seem to be.
Tried adding the multi-select tags to my share-config-custom.xml to define the item - it shows up, but I obviously can't seem to use the action ID to reference that action.
My next step was to try and create a js file with a registerAction function in it, which I am able to do and have it run (I can see the console.log dump) but I don't really have any idea how I would go about invoking my repo action from there).
How can I complete this task?
There is already function exist for invoking the repository custom action.This function is defined inside below file.
share-war\components\documentlibrary\actions.js
You can take reference of below code for invoking the repository action.
onActionSimpleRepoAction: function dlA_onActionSimpleRepoAction(record, owner)
{
//ACE-2470 : Clone: Clicking multiple times the simple Workflow approval menu item gives unexpected results.
if (owner.title.indexOf("_deactivated") == -1)
{
// Get action params
var params = this.getAction(record, owner).params,
displayName = record.displayName,
namedParams = ["function", "action", "success", "successMessage", "failure", "failureMessage", "async"],
repoActionParams = {};
for (var name in params)
{
if (params.hasOwnProperty(name) && !Alfresco.util.arrayContains(namedParams, name))
{
repoActionParams[name] = params[name];
}
}
//Deactivate action
var ownerTitle = owner.title;
owner.title = owner.title + "_deactivated";
var async = params.async ? "async=" + params.async : null;
// Prepare genericAction config
var config =
{
success:
{
event:
{
name: "metadataRefresh",
obj: record
}
},
failure:
{
message: this.msg(params.failureMessage, displayName),
fn: function showAction()
{
owner.title = ownerTitle;
},
scope: this
},
webscript:
{
method: Alfresco.util.Ajax.POST,
stem: Alfresco.constants.PROXY_URI + "api/",
name: "actionQueue",
queryString: async
},
config:
{
requestContentType: Alfresco.util.Ajax.JSON,
dataObj:
{
actionedUponNode: record.nodeRef,
actionDefinitionName: params.action,
parameterValues: repoActionParams
}
}
};
// Add configured success callbacks and messages if provided
if (YAHOO.lang.isFunction(this[params.success]))
{
config.success.callback =
{
fn: this[params.success],
obj: record,
scope: this
};
}
if (params.successMessage)
{
config.success.message = this.msg(params.successMessage, displayName);
}
// Acd configured failure callback and message if provided
if (YAHOO.lang.isFunction(this[params.failure]))
{
config.failure.callback =
{
fn: this[params.failure],
obj: record,
scope: this
};
}
if (params.failureMessage)
{
config.failure.message = this.msg(params.failureMessage, displayName);
}
// Execute the repo action
this.modules.actions.genericAction(config);
}
},
I am using keystone#0.2.32. I would like to change the post category to a tree structure. The below code is running well except when I create a category, it goes into a deadlock:
var keystone = require('keystone'),
Types = keystone.Field.Types;
/**
* PostCategory Model
* ==================
*/
var PostCategory = new keystone.List('PostCategory', {
autokey: { from: 'name', path: 'key', unique: true }
});
PostCategory.add({
name: { type: String, required: true },
parent: { type: Types.Relationship, ref: 'PostCategory' },
parentTree: { type: Types.Relationship, ref: 'PostCategory', many: true }
});
PostCategory.relationship({ ref: 'Post', path: 'categories' });
PostCategory.scanTree = function(item, obj, done) {
if(item.parent){
PostCategory.model.find().where('_id', item.parent).exec(function(err, cats) {
if(cats.length){
obj.parentTree.push(cats[0]);
PostCategory.scanTree(cats[0], obj, done);
}
});
}else{
done();
}
}
PostCategory.schema.pre('save', true, function (next, done) { //Parallel middleware, waiting done to be call
if (this.isModified('parent')) {
this.parentTree = [];
if(this.parent != null){
this.parentTree.push(this.parent);
PostCategory.scanTree(this, this, done);
}else
process.nextTick(done);
}else
process.nextTick(done); //here is deadlock.
next();
});
PostCategory.defaultColumns = 'name, parentTree';
PostCategory.register();
Thanks so much.
As I explained on the issue you logged on Keystone here: https://github.com/keystonejs/keystone/issues/759
This appears to be a reproducible bug in mongoose that prevents middleware from resolving when:
Parallel middleware runs that executes a query, followed by
Serial middleware runs that executes a query
Changing Keystone's autokey middleware to run in parallel mode may cause bugs in other use cases, so cannot be done. The answer is to implement your parentTree middleware in serial mode instead of parallel mode.
Also, some other things I noticed:
There is a bug in your middleware, where the first parent is added to the array twice.
The scanTree method would be better implemented as a method on the schama
You can use the findById method for a simpler parent query
The schema method looks like this:
PostCategory.schema.methods.addParents = function(target, done) {
if (this.parent) {
PostCategory.model.findById(this.parent, function(err, parent) {
if (parent) {
target.parentTree.push(parent.id);
parent.addParents(target, done);
}
});
} else {
done();
}
}
And the fixed middleware looks like this:
PostCategory.schema.pre('save', function(done) {
if (this.isModified('parent')) {
this.parentTree = [];
if (this.parent != null) {
PostCategory.scanTree(this, this, done);
} else {
process.nextTick(done);
}
} else {
process.nextTick(done);
}
});
I think it's a bug of keystone.js. I have changed schemaPlugins.js 104 line
from
this.schema.pre('save', function(next) {
to
this.schema.pre('save', true, function(next, done) {
and change from line 124 to the following,
// if has a value and is unmodified or fixed, don't update it
if ((!modified || autokey.fixed) && this.get(autokey.path)) {
process.nextTick(done);
return next();
}
var newKey = utils.slug(values.join(' ')) || this.id;
if (autokey.unique) {
r = getUniqueKey(this, newKey, done);
next();
return r;
} else {
this.set(autokey.path, newKey);
process.nextTick(done);
return next();
}
It works.
Please take a look at the screenshot:
As you can see there are 3 tabs with 3 different "index.xml" files opened.
I've been looking for an option to show something like "folder/file.extension" in the tabs name to be able to differentiate the files, but I can't find anything.
Using "Go to file" is not very helpful either, because the name of the path for all the files is so long that I can't see the folder containing the files.
Any ideas?
Cheers
UPDATE:
It's possible to increase the 'Go to file' panel width using the mouse and Komodo will remember the size in the future. That helps!
We added the OpenFiles pane specifically for this purpose, please check out View > Tabs & Sidebars > Open Files.
This is what my pane looks like with several files with the same name open:
Additionally you can specify your own patterns, currently this is done programmatically via a macro but in the future you will be able to do this through the UI.
For example, I use the following macro for Komodo development:
ko.openfiles.groupers.byPattern.patterns = [
{
name: 'Plat - %match% - config',
pattern: /\/skin\/plat\/([a-z0-9_-]*)\/_config\//i
},
{
name: 'Plat - %match%',
pattern: /\/skin\/plat\/([a-z0-9_-]*)\//i
},
{
name: 'Module - %match% - skin config',
pattern: /\/(?:module|modules)\/([a-z0-9_-]*)\/skin\/_config\//i
},
{
name: 'Module - %match%',
pattern: /\/(?:module|modules)\/([a-z0-9_-]*)\//i
},
{
name: 'Skin - %match% - config',
pattern: /\/chrome\/skins\/([a-z0-9_-]*)\/_config\//i
},
{
name: 'Skin - %match%',
pattern: /\/chrome\/skins\/([a-z0-9_-]*)\//i
},
{
name: 'Iconset - %match%',
pattern: /\/chrome\/iconsets\/([a-z0-9_-]*)\//i
},
{
name: 'Component - %match%',
pattern: /\/(?:component|components)\/([a-z0-9_-]*)/i
},
{
name: 'Locale',
pattern: /\/locale(?:\/|$)/i
},
{
name: 'Skin',
pattern: /\/skin(?:\/|$)/i
},
{
name: 'Module',
pattern: /\/(?:module|modules)(?:\/|$)/i
},
{
name: 'Component',
pattern: /\/(?:component|components)(?:\/|$)/i
},
];
ko.openfiles.reload(true);
You can read up on macro's here: http://docs.activestate.com/komodo/8.5/macros.html#macros_writing
Using the above macro I need to make sure I have the "Group By Pattern" option selected, then I just run the macro and the same files you see in my above screenshot will be grouped according to the patterns I specified:
Note that this requires the latest version of Komodo (8.5).
Also note that if you use the Open Files pane you may find that you do not need the regular tabs anymore, you can disable these under "View > View Editor Tabs".
Hope that helps, good luck!
Use the Komodo JavaScript API to change the default display of tab titles:
komodo.assertMacroVersion(3);
function changeTabTitles(useLongerTitle, splitLength) {
try {
var vm = ko.views.manager.topView;
var box = document.getAnonymousNodes(vm)[0];
// Get the views-tabbed elements.
var topTabs = box.firstChild;
var bottomTabs = box.lastChild;
if (!useLongerTitle) {
// Restore the original functionality.
if (topTabs._tweakui_updateLeafName) {
topTabs.updateLeafName = topTabs._tweakui_updateLeafName;
topTabs._tweakui_updateLeafName = null;
}
if (bottomTabs._tweakui_updateLeafName) {
bottomTabs.updateLeafName = bottomTabs._tweakui_updateLeafName;
bottomTabs._tweakui_updateLeafName = null;
}
} else {
// Save the original functionality.
if (!topTabs._tweakui_updateLeafName)
topTabs._tweakui_updateLeafName = topTabs.updateLeafName;
if (!bottomTabs._tweakui_updateLeafName)
bottomTabs._tweakui_updateLeafName = bottomTabs.updateLeafName;
// Replace the updateLeafName implementation to use something
// different for the tab label.
var osSvc = Components.classes["#activestate.com/koOs;1"].
getService(Components.interfaces.koIOs);
var dirsep = osSvc.sep;
topTabs.updateLeafName =
bottomTabs.updateLeafName = function(view) {
view.parentNode._tab.label = view.title;
if (view.document) {
view.parentNode._tab.setAttribute('crop', 'start');
var path = view.document.displayPath;
var sep = dirsep;
if (path.lastIndexOf(sep) == -1) {
// Try using the URI separator.
sep = "/";
}
var path_split = path.split(sep);
var l = path_split.length;
var label = path_split.slice(l-splitLength, l).join(sep);
view.parentNode._tab.label = label;
view.parentNode._tab.setAttribute('tooltiptext',view.document.displayPath);
this.tabbox.firstChild.scrollBoxObject.ensureElementIsVisible(this.tabbox.firstChild.selectedItem);
}
}
}
Save it as a macro and customize it to your liking.
code updated for Komodo 8.5 (view.document -> view.koDoc)
komodo.assertMacroVersion(3);
try {
var vm = ko.views.manager.topView;
var box = document.getAnonymousNodes(vm)[0];
// get the views-tabbed elements
var tabset1 = box.firstChild;
var tabset2 = box.lastChild;
// replace the updateLeafName implementation to use something different
// for the tab label
tabset1.updateLeafName =
tabset2.updateLeafName = function(view) {
view.parentNode._tab.label = view.title;
if (view.koDoc) {
var language = view.koDoc.language;
if (language == 'Python') {
var parts = view.koDoc.displayPath.split('/');
var len = parts.length;
var label = '';
if (len > 2) {
label += parts[len-2] + '/';
}
label += parts[len-1];
view.parentNode._tab.setAttribute('crop', 'start');
view.parentNode._tab.label = label;
view.parentNode._tab.setAttribute('tooltiptext',view.koDoc.displayPath);
this.tabbox.firstChild.scrollBoxObject.ensureElementIsVisible(this.tabbox.firstChild.selectedItem);
}
}
};
// the "on startup" trigger happens after files
// are opened, so we need to call updateLeafName
// for each opened view. Files opened after startup
// will be fine
var views = ko.views.manager.topView.getViews(true);
for (var i=0; i < views.length; i++) {
if (views[i].koDoc) {
views[i].updateLeafName(views[i]);
}
}
} catch(e) {
alert(e);
}
I want to use Qbs to compile an existing project. This project already contains a code-transformation-tool (my_tool) that is used heavily in this project.
So far I have (simplified):
import qbs 1.0
Project {
Application {
name: "my_tool"
files: "my_tool/main.cpp"
Depends { name: "cpp" }
}
Application {
name: "my_app"
Group {
files: 'main.cpp.in'
fileTags: ['cpp_in']
}
Depends { name: "cpp" }
Rule {
inputs: ["cpp_in"]
Artifact {
fileName: input.baseName
fileTags: "cpp"
}
prepare: {
var mytool = /* Reference to my_tool */;
var cmd = new Command(mytool, input.fileName, output.fileName);
cmd.description = "Generate\t" + input.baseName;
cmd.highlight = "codegen";
return cmd;
}
}
}
}
How can I get the reference to my_tool for the command?
This answer is based on an email from Qbs author Joerg Bornemann who allowed me to cite it here.
The property usings of Rule allows to add artifacts from products dependencies to the inputs.
In this case we are interested in "application" artifacts.
The list of applications could then be accessed as input.application.
Application {
name: "my_app"
Group {
files: 'main.cpp.in'
fileTags: ['cpp_in']
}
Depends { name: "cpp" }
// we need this dependency to make sure that my_tool exists before building my_app
Depends { name: "my_tool" }
Rule {
inputs: ["cpp_in"]
usings: ["application"] // dependent "application" products appear in inputs
Artifact {
fileName: input.completeBaseName
fileTags: "cpp"
}
prepare: {
// inputs["application"] is a list of "application" products
var mytool = inputs["application"][0].fileName;
var cmd = new Command(mytool, [inputs["cpp_in"][0].fileName, output.fileName]);
cmd.description = "Generate\t" + input.baseName;
cmd.highlight = "codegen";
return cmd;
}
}
}
Unfortunately the usings property in a Rule is deprecated since QBS 1.5.0. At the moment I have the same requirement. Using a product artifact in a non multiplex Rule.
The problem with a multiplex Rule is, if a single file in the input set changes, all input artifacts will be re-processed. Which is rather time consuming in my case.