2 title
: L
.tr('Package management'),
5 updateLists
: L
.rpc
.declare({
11 _allPackages
: L
.rpc
.declare({
14 params
: [ 'offset', 'limit', 'pattern' ],
18 _installedPackages
: L
.rpc
.declare({
20 method
: 'list_installed',
21 params
: [ 'offset', 'limit', 'pattern' ],
25 _findPackages
: L
.rpc
.declare({
28 params
: [ 'offset', 'limit', 'pattern' ],
32 _fetchPackages: function(action
, offset
, limit
, pattern
)
36 return action(offset
, limit
, pattern
).then(function(list
) {
37 if (!list
.total
|| !list
.packages
)
38 return { length
: 0, total
: 0 };
40 packages
.push
.apply(packages
, list
.packages
);
41 packages
.total
= list
.total
;
46 if (packages
.length
>= limit
)
51 for (var i
= offset
+ packages
.length
; i
< limit
; i
+= 100)
52 action(i
, (Math
.min(i
+ 100, limit
) % 100) || 100, pattern
);
55 }).then(function(lists
) {
56 for (var i
= 0; i
< lists
.length
; i
++)
58 if (!lists
[i
].total
|| !lists
[i
].packages
)
61 packages
.push
.apply(packages
, lists
[i
].packages
);
62 packages
.total
= lists
[i
].total
;
69 listPackages: function(offset
, limit
, pattern
)
71 return this._fetchPackages(this._allPackages
, offset
, limit
, pattern
);
74 installedPackages: function(offset
, limit
, pattern
)
76 return this._fetchPackages(this._installedPackages
, offset
, limit
, pattern
);
79 findPackages: function(offset
, limit
, pattern
)
81 return this._fetchPackages(this._findPackages
, offset
, limit
, pattern
);
84 installPackage
: L
.rpc
.declare({
87 params
: [ 'package' ],
91 removePackage
: L
.rpc
.declare({
94 params
: [ 'package' ],
98 getConfig
: L
.rpc
.declare({
100 method
: 'config_get',
101 expect
: { config
: '' }
104 setConfig
: L
.rpc
.declare({
105 object
: 'luci2.opkg',
106 method
: 'config_set',
110 isInstalled: function(pkg
)
112 return this._installedPackages(0, 1, pkg
).then(function(list
) {
113 return (!isNaN(list
.total
) && list
.total
> 0);
118 updateDiskSpace: function()
120 return L
.system
.getDiskInfo().then(function(info
) {
121 $('#package_space').empty().append(
123 value
: info
.root
.used
/ 1024,
124 max
: info
.root
.total
/ 1024,
125 format
: '%d ' + L
.tr('kB') + ' / %d ' + L
.tr('kB') + ' ' + L
.trc('Used disk space', 'used') + ' (%d%%)'
130 installRemovePackage: function(pkgname
, installed
)
134 var dspname
= pkgname
.replace(/^.+\//, '');
135 var action
= installed
? self
.opkg
.removePackage
: self
.opkg
.installPackage
;
136 var title
= (installed
? L
.tr('Removing package "%s" …') : L
.tr('Installing package "%s" …')).format(dspname
);
137 var confirm
= (installed
? L
.tr('Really remove package "%h" ?') : L
.tr('Really install package "%h" ?')).format(dspname
);
139 L
.ui
.dialog(title
, confirm
, {
141 confirm: function() {
142 L
.ui
.dialog(title
, L
.tr('Waiting for package manager …'), { style
: 'wait' });
144 action
.call(self
.opkg
, pkgname
).then(function(res
) {
145 self
.fetchInstalledList().then(function() { return self
.fetchPackageList(); }).then(function() {
149 output
.push($('<pre />').text(res
.stdout
));
152 output
.push($('<pre />').addClass('alert-message').text(res
.stderr
));
154 output
.push(res
.code
? L
.tr('Package manager failed with status %d.').format(res
.code
) : L
.tr('Package manager finished successfully.'));
156 L
.ui
.dialog(title
, output
, { style
: 'close' });
159 $('#package_url').val('');
166 fetchInstalledList: function()
169 return self
.opkg
.installedPackages(0, 0, '*').then(function(list
) {
170 self
.installedList
= { };
171 for (var i
= 0; i
< list
.length
; i
++)
172 self
.installedList
[list
[i
][0]] = true;
176 fetchPackageList: function(offset
, interactive
)
181 if (typeof(offset
) == 'undefined')
182 offset
= parseInt($('#package_filter').attr('offset')) || 0;
186 var pattern
= $('#package_filter').val() || '';
191 action
= $('#package_which').prop('checked') ? self
.opkg
.installedPackages
: self
.opkg
.findPackages
;
192 pattern
= '*' + pattern
+ '*';
194 $('#package_filter').next().attr('src', L
.globals
.resource
+ '/icons/cbi/remove.gif');
198 action
= $('#package_which').prop('checked') ? self
.opkg
.installedPackages
: self
.opkg
.listPackages
;
201 $('#package_filter').next().attr('src', L
.globals
.resource
+ '/icons/cbi/find.gif');
204 $('#package_filter').attr('offset', offset
);
206 var install_disabled
= $('#package_install').attr('disabled');
208 return action
.call(self
.opkg
, offset
, 100, pattern
).then(function(list
) {
209 var packageTable
= new L
.ui
.table({
210 placeholder
: L
.tr('No matching packages found.'),
212 caption
: L
.trc('Package table header', 'Package'),
215 caption
: L
.trc('Package table header', 'Version'),
217 format: function(v
) {
218 return (v
.length
> 15 ? v
.substring(0, 14) + '…' : v
);
221 caption
: L
.trc('Package table header', 'Description'),
224 caption
: L
.trc('Package table header', 'Installation Status'),
227 format: function(v
, n
) {
228 var inst
= self
.installedList
[list
[n
][0]];
229 return L
.ui
.button(inst
? L
.trc('Package state', 'Installed') : L
.trc('Package state', 'Not installed'), inst
? 'success' : 'danger')
230 .css('width', '100%')
231 .attr('disabled', install_disabled
)
232 .attr('pkgname', list
[n
][0])
233 .attr('installed', inst
)
235 self
.installRemovePackage(this.getAttribute('pkgname'), this.getAttribute('installed') == 'true');
241 packageTable
.rows(list
);
242 packageTable
.insertInto('#package_table');
246 .attr('offset', offset
- 100)
247 .attr('disabled', false)
248 .text('« %d - %d'.format(offset
- 100 + 1, offset
));
251 .attr('disabled', true)
252 .text('« %d - %d'.format(1, Math
.min(100, list
.total
)));
254 if ((offset
+ 100) < list
.total
)
256 .attr('offset', offset
+ 100)
257 .attr('disabled', false)
258 .text('%d - %d »'.format(offset
+ 100 + 1, Math
.min(offset
+ 200, list
.total
)));
261 .attr('disabled', true)
262 .text('%d - %d »'.format(list
.total
- (list
.total
% 100) + 1, list
.total
));
266 }).then(self
.updateDiskSpace
);
273 $('textarea, input.cbi-button-save').attr('disabled', !this.options
.acls
.software
);
274 $('#package_update, #package_url, #package_install').attr('disabled', !this.options
.acls
.software
);
277 self
.opkg
.getConfig().then(function(config
) {
278 $('#config textarea')
279 .attr('rows', (config
.match(/\n/g) || [ ]).length
+ 1)
284 var data
= ($('#config textarea').val() || '').replace(/\r/g, '').replace(/\n?$/, '\n');
286 self
.opkg
.setConfig(data
).then(function() {
287 $('#config textarea')
288 .attr('rows', (data
.match(/\n/g) || [ ]).length
+ 1)
295 self
.fetchInstalledList(),
296 self
.updateDiskSpace()
298 $('#package_prev, #package_next').click(function(ev
) {
299 if (!this.getAttribute('disabled'))
301 self
.fetchPackageList(parseInt(this.getAttribute('offset')), true);
306 $('#package_filter').next().click(function(ev
) {
307 $('#package_filter').val('');
308 self
.fetchPackageList(0, true);
311 $('#package_filter').keyup(function(ev
) {
316 self
.fetchPackageList(0, true);
320 $('#package_which').click(function(ev
) {
322 self
.fetchPackageList(0, true);
325 $('#package_url').keyup(function(ev
) {
332 self
.installRemovePackage(this.value
, false);
335 $('#package_install').click(function(ev
) {
336 var name
= $('#package_url').val();
338 self
.installRemovePackage(name
, false);
341 $('#package_update').click(function(ev
) {
342 L
.ui
.dialog(L
.tr('Updating package lists'), L
.tr('Waiting for package manager …'), { style
: 'wait' });
343 self
.opkg
.updateLists().then(function(res
) {
347 output
.push($('<pre />').text(res
.stdout
));
350 output
.push($('<pre />').addClass('alert-message').text(res
.stderr
));
352 output
.push(res
.code
? L
.tr('Package manager failed with status %d.').format(res
.code
) : L
.tr('Package manager finished successfully.'));
354 L
.ui
.dialog(L
.tr('Updating package lists'), output
, { style
: 'close' });
358 return self
.fetchPackageList(0);