json-object-editor 0.10.653 → 0.10.657

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -168,6 +168,27 @@ function JsonObjectEditor(specs){
168
168
  }});
169
169
  }
170
170
 
171
+ };
172
+ this.uploaderSetFileRole = function(uploader_id, filename, role){
173
+ try{
174
+ var joe_uploader = self.uploaders && self.uploaders[uploader_id];
175
+ if (!joe_uploader || !Array.isArray(joe_uploader.files)) { return; }
176
+ var file = joe_uploader.files.where({filename:filename})[0];
177
+ if (!file) { return; }
178
+ if (role) {
179
+ file.file_role = role;
180
+ } else {
181
+ delete file.file_role;
182
+ }
183
+ if (joe_uploader.preview) {
184
+ $(joe_uploader.preview).html(_renderUploaderFilePreviews(joe_uploader.files, joe_uploader.cuid));
185
+ }
186
+ if (typeof self.updateObject === 'function') {
187
+ self.updateObject(null, null, true);
188
+ }
189
+ }catch(e){
190
+ console.warn('uploaderSetFileRole error', e && e.message || e);
191
+ }
171
192
  };
172
193
  window._joes.push(this);
173
194
 
@@ -5329,6 +5350,19 @@ this.renderHTMLContent = function(specs){
5329
5350
  if(files && !$c.isArray(files)){
5330
5351
  files = [files];
5331
5352
  }
5353
+
5354
+ // Register uploader object (including optional file_roles) BEFORE we render
5355
+ // previews so that _renderUploaderFilePreviews can access role metadata.
5356
+ var uploader_obj = {
5357
+ cuid: uploader_id,
5358
+ prop: prop.name,
5359
+ files: files,
5360
+ max: prop.max || 0,
5361
+ // Optional file role configuration from schema (e.g. [{value,label,default}])
5362
+ roles: prop.file_roles || []
5363
+ };
5364
+ self.uploaders[uploader_id] = uploader_obj;
5365
+
5332
5366
  if(prop.field && self.current.object[prop.field]){
5333
5367
  var kfield = self.getField(prop.field);
5334
5368
  var preview = self.current.object[prop.field]
@@ -5351,18 +5385,11 @@ this.renderHTMLContent = function(specs){
5351
5385
  '<div class="joe-uploader-preview">'+dz_message+'</div>'+
5352
5386
  '</div>'+
5353
5387
  '<div class="joe-uploader-message">add a file</div>'+
5354
- '<div class="joe-button joe-green-button joe-upload-cofirm-button hidden" onclick="_joe.uploaderConfirm(\''+uploader_id+'\',\''+prop.name+'\');">Upload File</div>'+__clearDiv__+
5388
+ '<div class="joe-button joe-green-button joe-upload-cofirm-button ui-helper-hidden hidden" onclick="_joe.uploaderConfirm(\''+uploader_id+'\',\''+prop.name+'\');">Upload File</div>'+__clearDiv__+
5355
5389
  '</div>'
5356
5390
  ;
5357
5391
  //var idprop = prop.idprop || '_id';
5358
5392
  //html+= __clearDiv__;
5359
- var uploader_obj = {
5360
- cuid:uploader_id,
5361
- prop:prop.name,
5362
- files:files,
5363
- max:prop.max || 0
5364
- }
5365
- self.uploaders[uploader_id] = uploader_obj;
5366
5393
  return html;
5367
5394
  };
5368
5395
  function _removeUploaderFile(id,filename){
@@ -5378,9 +5405,10 @@ this.renderHTMLContent = function(specs){
5378
5405
  var jup_template,html= '';
5379
5406
  var alink ="<a href='${url}${base64}' class='file-link' target='_blank'></a>";
5380
5407
  var delete_btn = '<joe-uploader-file-delete-btn class="svg-shadow" onclick="_joe.Fields.uploader.remove(\''+cuid+'\',\'${filename}\');">'+self.SVG.icon.close+'</joe-uploader-delete-btn>';
5381
- var label = '<joe-uploader-file-label>${filename}</joe-uploader-file-label>';
5382
-
5383
-
5408
+ var label = '';
5409
+ var uploader = self.uploaders && self.uploaders[cuid];
5410
+ var roles = uploader && uploader.roles;
5411
+
5384
5412
  files.map(function(file){
5385
5413
  var filesize = '<joe-uploader-file-label>${filename}</joe-uploader-file-label>';
5386
5414
  if(file.size){
@@ -5403,18 +5431,49 @@ this.renderHTMLContent = function(specs){
5403
5431
 
5404
5432
 
5405
5433
 
5434
+ // Build the label with filename and optional OpenAI file id on a new line
5435
+ label = '<joe-uploader-file-label>'+(file.filename || '')+'</joe-uploader-file-label>';
5436
+ if(file.openai_file_id){
5437
+ label += '<joe-uploader-file-oaid >'+file.openai_file_id+'</joe-uploader-file-oaid>';
5438
+ }
5439
+
5440
+ // Optional file role selector when roles are configured on this uploader
5441
+ var roleSelect = '';
5442
+ if (roles && roles.length) {
5443
+ var currentRole = file.file_role || '';
5444
+ var safeFilename = String(file.filename || '').replace(/'/g, "\\'");
5445
+ var optsHtml = '<option value=""></option>';
5446
+ roles.forEach(function(r){
5447
+ if (!r || !r.value) { return; }
5448
+ var lbl = r.label || r.value;
5449
+ var sel = (currentRole === r.value) ? ' selected' : '';
5450
+ optsHtml += '<option value="'+r.value.replace(/\"/g,'&quot;')+'"'+sel+'>'+lbl.replace(/</g,'&lt;').replace(/>/g,'&gt;')+'</option>';
5451
+ });
5452
+ var onchange = "_joe.uploaderSetFileRole('"+cuid+"','"+safeFilename+"', this.value)";
5453
+ roleSelect = '<div class=\"joe-uploader-file-role\"><select onchange=\"'+onchange+'\">'+optsHtml+'</select></div>';
5454
+ }
5455
+
5406
5456
  if((file.type && file.type.contains('image')) || (!file.type && (file.url||file.base64).split('.').contains(['jpg','jpeg','png','gif','svg']))){
5407
- jup_template = '<joe-uploader-file class="'+(file.uploaded && 'uploaded'||'')+'" style="background-image:url(${url}${base64})">'+alink+label+filesize+delete_btn+'</joe-uploader-file>';
5457
+ jup_template = '<joe-uploader-file class="'+(file.uploaded && 'uploaded'||'')+'" style="background-image:url(${url}${base64})">'+alink+label+roleSelect+filesize+delete_btn+'</joe-uploader-file>';
5408
5458
  }else if (docTypes.includes((file.url || file.base64).split('.').pop())) {
5409
5459
  jup_template = '<joe-uploader-file class="'+(file.uploaded && 'uploaded'||'')+'">'+
5410
5460
  '<joe-uploader-file-extension >.'+(file.type.split('/')[1] || 'docx')+'</joe-uploader-file-extension>'+
5411
- alink+label+filesize+delete_btn+'</joe-uploader-file>';
5461
+ alink+label+roleSelect+filesize+delete_btn+'</joe-uploader-file>';
5412
5462
  }else{
5413
5463
  jup_template = '<joe-uploader-file class="'+(file.uploaded && 'uploaded'||'')+'">'+
5414
5464
  '<joe-uploader-file-extension >.'+(file.type.split('/')[1] || '???')+'</joe-uploader-file-extension>'+
5415
- alink+label+filesize+delete_btn+'</joe-uploader-file>';
5465
+ alink+label+roleSelect+filesize+delete_btn+'</joe-uploader-file>';
5416
5466
  }
5417
- html += fillTemplate(jup_template,file);
5467
+ // Build optional OpenAI retry/upload button
5468
+ var openaiBtn = '';
5469
+ var needRetry = (!file.openai_file_id || file.openai_status == 'error');
5470
+ var hasUrl = !!file.url;
5471
+ if(hasUrl && needRetry){
5472
+ var openaiLabel = (file.openai_status == 'error') ? 'Retry OpenAI' : 'Upload to OpenAI';
5473
+ var safeFilename = (file.filename || '').replace(/'/g,"\\'");
5474
+ openaiBtn = '<joe-uploader-openai-btn class="joe-button" style="margin-left:6px;" onclick="_joe.SERVER.Plugins.openaiRetryFromUrl(\''+cuid+'\',\''+safeFilename+'\');">'+openaiLabel+'</joe-uploader-openai-btn>';
5475
+ }
5476
+ html += fillTemplate(jup_template,file) + openaiBtn;
5418
5477
  })
5419
5478
  return html+'<div class="clear"></div>';
5420
5479
  }
@@ -5431,7 +5490,7 @@ this.renderHTMLContent = function(specs){
5431
5490
  $(joe_uploader.dropzone).find('img,div').replaceWith('<img src="' + base64 + '">')
5432
5491
  //joe_uploader.dropzone.html('<img src="' + base64 + '">');
5433
5492
  joe_uploader.message.html('<b>'+file.name+'</b> selected');
5434
- joe_uploader.confirmBtn.removeClass('hidden');
5493
+ joe_uploader.confirmBtn.removeClass('ui-helper-hidden hidden');
5435
5494
  }else {
5436
5495
  results.innerHTML = 'Nothing to upload.';
5437
5496
  }
@@ -5462,7 +5521,7 @@ this.renderHTMLContent = function(specs){
5462
5521
  }
5463
5522
  joe_uploader.files.push(temp_file);
5464
5523
  $(joe_uploader.preview).html(_renderUploaderFilePreviews(joe_uploader.files,joe_uploader.cuid));
5465
- joe_uploader.confirmBtn.removeClass('hidden');
5524
+ joe_uploader.confirmBtn.removeClass('ui-helper-hidden hidden');
5466
5525
  }else {
5467
5526
  results.innerHTML = 'Nothing to upload.';
5468
5527
  }
@@ -5530,6 +5589,20 @@ this.renderHTMLContent = function(specs){
5530
5589
  var fileobj = joe_uploader.files.where({filename:filename})[0];
5531
5590
  fileobj.uploaded = new Date();
5532
5591
  fileobj.url = url;
5592
+ // Apply captured OpenAI info if present
5593
+ var key = self.current.object._id + '/' + filename;
5594
+ var oi = _joe._lastOpenAI && _joe._lastOpenAI[key];
5595
+ fileobj.openai_purpose = 'assistants';
5596
+ if(oi){
5597
+ fileobj.openai_file_id = oi.openai_file_id || null;
5598
+ fileobj.openai_status = (oi.openai_file_id && 'ok') || (oi.openai_error && 'error') || 'not_uploaded';
5599
+ fileobj.openai_error = oi.openai_error || null;
5600
+ if(oi.openai_error){
5601
+ joe_uploader.message.append('<div>OpenAI error: '+oi.openai_error+'</div>');
5602
+ }
5603
+ }else{
5604
+ fileobj.openai_status = fileobj.openai_status || 'not_uploaded';
5605
+ }
5533
5606
  delete fileobj.base64;
5534
5607
  }
5535
5608
 
@@ -5600,6 +5673,14 @@ this.renderHTMLContent = function(specs){
5600
5673
  self.uploaders[id].dropzone = $(this).find('.joe-uploader-dropzone');
5601
5674
  self.uploaders[id].confirmBtn = $(this).find('.joe-upload-cofirm-button');
5602
5675
 
5676
+ // If there are pending files (not uploaded or missing url), show the confirm button
5677
+ var pending = (self.uploaders[id].files || []).some(function(file){
5678
+ return $c.isObject(file) && (!file.uploaded || !file.url);
5679
+ });
5680
+ if(pending){
5681
+ self.uploaders[id].confirmBtn.removeClass('ui-helper-hidden hidden');
5682
+ }
5683
+
5603
5684
  });
5604
5685
  };
5605
5686
  encapsulateFieldType('uploader',self.renderUploaderField,
@@ -9936,6 +10017,13 @@ logit(intent)
9936
10017
  url = prefix+response.Key;
9937
10018
  }
9938
10019
  var filename = response.Key.replace(self.current.object._id+'/','');
10020
+ // Stash OpenAI info for use during finalization
10021
+ _joe._lastOpenAI = _joe._lastOpenAI || {};
10022
+ _joe._lastOpenAI[response.Key] = {
10023
+ openai_file_id: response.openai_file_id,
10024
+ openai_purpose: response.openai_purpose,
10025
+ openai_error: response.openai_error
10026
+ };
9939
10027
  if(field.url_field){
9940
10028
  var nprop = {};
9941
10029
  _joe.current.object[field.url_field] = url;
@@ -9960,6 +10048,53 @@ logit(intent)
9960
10048
  file: { base64:data.base64, extension:data.extension, type:data.contentType, filename: (_joe.current.object._id+data.extension), name: (_joe.current.object._id+data.extension) },
9961
10049
  field: data.field
9962
10050
  }, callback);
10051
+ },
10052
+ // Retry uploading a specific file to OpenAI using its URL.
10053
+ // Invoked by the uploader row action.
10054
+ openaiRetryFromUrl:function(uploader_id, filename){
10055
+ try{
10056
+ var joe_uploader = self.uploaders[uploader_id];
10057
+ if(!joe_uploader){ return alert('Uploader not found'); }
10058
+ var file = joe_uploader.files.where({filename:filename})[0];
10059
+ if(!file){ return alert('File not found'); }
10060
+ if(!file.url){ return alert('No URL to upload to OpenAI'); }
10061
+ var btn = $(joe_uploader.preview).find("joe-uploader-openai-btn:contains('"+filename+"')");
10062
+ joe_uploader.message.append('<div>Uploading to OpenAI...</div>');
10063
+ $.ajax({
10064
+ url: (location.port && '//'+__jsc.hostname+':'+__jsc.port+'/API/plugin/chatgpt/filesRetryFromUrl/') || '//'+__jsc.hostname+'/API/plugin/chatgpt/filesRetryFromUrl/',
10065
+ type:'POST',
10066
+ data:{
10067
+ url:file.url,
10068
+ filename:file.filename,
10069
+ contentType:file.type
10070
+ },
10071
+ success:function(resp){
10072
+ if(resp && resp.success && resp.openai_file_id){
10073
+ file.openai_file_id = resp.openai_file_id;
10074
+ file.openai_purpose = resp.openai_purpose || 'assistants';
10075
+ file.openai_status = 'ok';
10076
+ file.openai_error = null;
10077
+ joe_uploader.message.append('<div>OpenAI file attached</div>');
10078
+ }else{
10079
+ file.openai_status = 'error';
10080
+ file.openai_error = (resp && resp.error) || 'Upload failed';
10081
+ joe_uploader.message.append('<div>OpenAI error: '+(file.openai_error)+'</div>');
10082
+ }
10083
+ // Refresh preview to reflect new status/button
10084
+ $(joe_uploader.preview).html(_renderUploaderFilePreviews(joe_uploader.files,joe_uploader.cuid));
10085
+ // Save object so file metadata is persisted
10086
+ self.updateObject(null, null, true);
10087
+ },
10088
+ error:function(xhr,status,err){
10089
+ var message = (xhr && (xhr.responseJSON && (xhr.responseJSON.error || xhr.responseJSON.code) || xhr.responseText)) || err || status || 'Retry failed';
10090
+ file.openai_status = 'error';
10091
+ file.openai_error = message;
10092
+ joe_uploader.message.append('<div>OpenAI error: '+message+'</div>');
10093
+ }
10094
+ });
10095
+ }catch(e){
10096
+ alert(e && e.message || e);
10097
+ }
9963
10098
  }
9964
10099
  },
9965
10100
  ready:{