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);