├── .gitignore
├── LICENSE
├── README.md
├── fpsoftware.xml
├── front
├── config.form.php
└── user_softwarelicense.form.php
├── hook.php
├── inc
├── common.class.php
├── config.class.php
├── licensehelper.class.php
├── userdetails.class.php
├── userslicenses.class.php
└── versionhelper.class.php
└── setup.php
/.gitignore:
--------------------------------------------------------------------------------
1 | # IDE files
2 | .idea/
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Future Processing Sp. z o.o.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # glpi-fpsoftware
2 | GLPI plugin that allows to assign software to users.
3 |
4 | ## General Information
5 | By default GLPI does not allow to assign software to a user. As we all know a lot of software is currently licensed in per user model. FP Software plugin allows you to assign software to a user and view all his/hers licenses.
6 |
7 | It changes how software/license asset looks like by providing new tab to license where you can assign users to software and another tab to software so you can see all users using the software (no matter which licenses they are using).
8 |
9 | Global view of all licenses lists all users along with their machines, in that case if user has more than one PC he/she is listed more than once. This view can be modified to aggregate computers per license per user, by switching plugin's configuration option "Calculate licenses number per user instead of per computer". To do that, please go to plugins list, click "FP Software" plugin name and select desired option.
10 |
11 |
12 | 
13 |
14 | **(License view – new “Users” tab)**
15 |
16 | 
17 |
18 | **(Software view – new “Licenses – Users” tab)**
19 |
20 | 
21 |
22 | **(User view – new “Licenses” tab)**
23 |
24 | In general this plugin makes GLPI more user-oriented instead of PC-oriented.
25 |
26 | ### Requirements
27 | GLPI 0.85.x, 0.90.x, 9.1.x - 9.4.x
28 |
29 | ### Install instructions
30 | 1. Copy plugin to plugins directory.
31 | 2. Install/enable plugin from Administration/Plugins
32 | section.
33 |
34 | Please be sure that name of the folder that contains plugin file is fpsoftware.
35 |
36 | If you want to display a sum of assigned computers and users in "Affected Computers" column you need to:
37 | - open inc/softwwarelicense.class.php (take a backup of this file before making any changes!);
38 | - look for following function - static function showForSoftware (Software $software);
39 | - around lines 780-787 modify following line of code:
40 |
41 | ```
42 | $nb_assoc = Computer_SoftwareLicense::countForLicense($data['id']);
43 | ```
44 |
45 | to
46 |
47 | ```
48 | $nb_assoc = Computer_SoftwareLicense::countForLicense($data['id']) + PluginFpsoftwareCommon::countForLicense($data['id']);
49 | ```
50 |
51 | ### What can be improved?
52 | * plugin does not validate total number of licenses with number of assigned licenses to users;
53 |
--------------------------------------------------------------------------------
/fpsoftware.xml:
--------------------------------------------------------------------------------
1 |
2 | FP Software
3 | fpsoftware
4 | stable
5 | https://cloud.githubusercontent.com/assets/7323944/10058664/fc694b1e-6246-11e5-9047-64583a3d04ae.png
6 |
7 |
8 |
9 |
10 |
11 | It
13 | changes how software/license asset looks like by providing new tab to license where you can assign users to software and
14 | another tab to software so you can see all users using the software (no matter which licenses they are using).
In
15 | general this plugin makes GLPI more user-oriented instead of
16 | PC-oriented.
Changelog:
17 | 2022.01:
18 | - added sorting to both licenses in Software and User sections;
19 | - improved adding users to licenses by excluding users who are already assigned to it;
20 | - added validation - assigned user vs. no. of available licenses
21 | 2017.05:
22 | - plugin is now compatible with GLPI 9.1.3
23 | 2017.04:
24 | - plugin is now compatible with GLPI 9.1.2
25 | 2015.12:
26 | - Licenses - Users tab now shows proper per user license usage and groups PCs owned by a user;
27 | - "Affected Computers" can show how many licenses are assigned to both users and computers (please read manual on our GitHub)
28 | - plugin is now compatible with GLPI 0.90
29 | Company website]]>
30 |
31 |
32 | https://github.com/FutureProcessing/glpi-fpsoftware
33 | https://github.com/FutureProcessing/glpi-fpsoftware/archive/refs/tags/v.1.6.1.zip
34 | https://github.com/FutureProcessing/glpi-fpsoftware/issues
35 | https://github.com/FutureProcessing/glpi-fpsoftware/blob/master/README.md
36 |
37 | Future Processing
38 |
39 |
40 |
41 | 1.6.1
42 | 9.5.x
43 |
44 |
45 | 1.4
46 | 9.4.x
47 |
48 |
49 | 1.3
50 | 9.3.x
51 |
52 |
53 | 1.2
54 | 9.1.3
55 |
56 |
57 | 1.1
58 | 9.1.2
59 | 9.1.1
60 | 9.1
61 |
62 |
63 | 1.0
64 | 0.90
65 | 0.85
66 |
67 |
68 |
69 | en_GB
70 |
71 |
72 |
73 |
74 | Inventory
75 |
76 |
77 | Inventory
78 |
79 |
80 |
81 | https://cloud.githubusercontent.com/assets/3634020/8588884/ca0d363a-260f-11e5-9a7c-8be8b4600eb2.png
82 | https://cloud.githubusercontent.com/assets/3634020/8588883/ca0995ca-260f-11e5-9e55-31dd860081ea.png
83 | https://cloud.githubusercontent.com/assets/3634020/8588885/ca1083b2-260f-11e5-85e3-0182aa70e4b4.png
84 |
85 |
86 |
--------------------------------------------------------------------------------
/front/config.form.php:
--------------------------------------------------------------------------------
1 | (int)(bool)$_POST['group_by_users']));
11 | }
12 | HTML::back();
13 | }
14 |
15 |
16 | HTML::header('Configuration of FP Software plugin');
17 |
18 | $config->showFormDisplay();
19 |
20 | HTML::footer();
21 |
22 |
--------------------------------------------------------------------------------
/front/user_softwarelicense.form.php:
--------------------------------------------------------------------------------
1 | 0 ) {
11 | if ($usl->add($_POST)) {
12 | Glpi\Event::log($_POST['softwarelicenses_id'], "softwarelicense", 4, "inventory",
13 | //TRANS: %s is the user login
14 | sprintf(__('%s associates an user and a license'), $_SESSION["glpiname"]));
15 | }
16 | }
17 | Html::back();
18 |
19 | }
20 |
21 | Html::displayErrorAndDie('Lost');
22 |
23 | ?>
--------------------------------------------------------------------------------
/hook.php:
--------------------------------------------------------------------------------
1 | .
25 |
26 | ------------------------------------------------------------------------
27 |
28 | @package FPFutures
29 | @author Future Processing
30 | @co-author
31 | @copyright Copyright (c) 2014 by Future Processing
32 | @license AGPL License 3.0 or (at your option) any later version
33 | http://www.gnu.org/licenses/agpl-3.0-standalone.html
34 | @since 2014
35 |
36 | ------------------------------------------------------------------------
37 | */
38 |
39 | /**
40 | * It is in these functions that you need to put your SQL queries used for creating your specific tables.
41 | *
42 | * Here, you can now see your plugin in the list of plugins.
43 | *
44 | * @return boolean Needs to return true if success
45 | */
46 |
47 | function plugin_fpsoftware_install() {
48 | global $DB;
49 |
50 | if ( ! $DB->tableExists("glpi_users_softwarelicenses")) {
51 | $query = "CREATE TABLE IF NOT EXISTS `glpi_users_softwarelicenses` (
52 | `id` int(11) NOT NULL AUTO_INCREMENT,
53 | `users_id` int(11) NOT NULL,
54 | `softwarelicenses_id` int(11) NOT NULL,
55 | `added` datetime NOT NULL,
56 | PRIMARY KEY (`id`)
57 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
58 |
59 | $DB->queryOrDie($query, "create glpi_users_softwarelicenses table");
60 | }
61 |
62 | return true;
63 | }
64 |
65 | /**
66 | * Because we've created a table, do not forget to destroy if the plugin is uninstalled.
67 | *
68 | * @return boolean Needs to return true if success
69 | */
70 | function plugin_fpsoftware_uninstall() {
71 | global $DB;
72 |
73 | if ($DB->tableExists("glpi_users_softwarelicenses")) {
74 | $query = "DROP TABLE glpi_users_softwarelicenses;";
75 |
76 | $DB->queryOrDie($query, "drop glpi_users_softwarelicenses table");
77 | }
78 |
79 | return true;
80 | }
81 |
--------------------------------------------------------------------------------
/inc/common.class.php:
--------------------------------------------------------------------------------
1 | getTable();
24 | $tab[2]['field'] = 'id';
25 | $tab[2]['name'] = __('ID');
26 | $tab[2]['massiveaction'] = false;
27 | $tab[2]['datatype'] = 'number';
28 |
29 | $tab[4]['table'] = 'glpi_softwarelicenses';
30 | $tab[4]['field'] = 'name';
31 | $tab[4]['name'] = _n('License', 'Licenses', 1);
32 | $tab[4]['datatype'] = 'dropdown';
33 | $tab[4]['massiveaction'] = false;
34 |
35 | $tab[5]['table'] = 'glpi_users';
36 | $tab[5]['field'] = 'name';
37 | $tab[5]['name'] = _n('User', 'Users', 1);
38 | $tab[5]['massiveaction'] = false;
39 | $tab[5]['datatype'] = 'dropdown';
40 |
41 | return $tab;
42 | }
43 |
44 | /**
45 | * Add relationship user -> license to database
46 | * @param array $input
47 | * @param array $options
48 | * @param bool $history
49 | * @return bool
50 | */
51 | function add(array $input, $options=array(), $history=true) {
52 | if ( (int) $input['softwarelicenses_id'] <= 0 || (int) $input['users_id'] <= 0) {
53 | return false;
54 | }
55 |
56 | global $DB;
57 |
58 | $users_id = (int) $input['users_id'];
59 | $softwarelicenses_id = (int) $input['softwarelicenses_id'];
60 | $added = date("Y-m-d H:i:s");
61 |
62 | $query = "INSERT INTO glpi_users_softwarelicenses (users_id, softwarelicenses_id,added) VALUES($users_id,$softwarelicenses_id,'$added')";
63 | $DB->query($query);
64 |
65 | return true;
66 | }
67 |
68 | /**
69 | * @see CommonDBTM::processMassiveActionsForOneItemtype()
70 | * @param MassiveAction $ma
71 | * @param CommonDBTM $item
72 | * @param array $ids
73 | * @return void
74 | */
75 | static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item, array $ids) {
76 | switch ($ma->getAction()) {
77 | case 'deleteSelected':
78 | if (isset($_POST['items']['PluginFpsoftwareCommon']) && is_array($_POST['items']['PluginFpsoftwareCommon'])) {
79 | foreach($_POST['items']['PluginFpsoftwareCommon'] as $id => $val) {
80 | self::deleteItem($id);
81 | $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
82 | }
83 | }
84 | break;
85 | }
86 | }
87 |
88 | /**
89 | * @see CommonDBTM::doSpecificMassiveActions()
90 | * @param array $input
91 | * @return array
92 | */
93 | function doSpecificMassiveActions($input=array()) {
94 | $res = array(
95 | 'ok' => 0,
96 | 'ko' => 0,
97 | 'noright' => 0
98 | );
99 | switch ($input['action']) {
100 | case 'deleteSelected':
101 | if (isset($_POST['itemtype'])
102 | && $_POST['itemtype'] == 'PluginFpsoftwareCommon'
103 | && isset($_POST['item'])
104 | && is_array($_POST['item'])) {
105 | foreach($_POST['item'] as $id => $val) {
106 | self::deleteItem($id);
107 | $res['ok']++;
108 | }
109 | } else {
110 | $res['ko']++;
111 | }
112 | break;
113 | default :
114 | return parent::doSpecificMassiveActions($input);
115 | }
116 | return $res;
117 | }
118 |
119 |
120 |
121 | /**
122 | * Delete from database
123 | * @param int $id
124 | */
125 | static function deleteItem($id) {
126 | global $DB;
127 |
128 | $id = (int) $id;
129 |
130 | if ($id > 0) {
131 | $query = "DELETE FROM glpi_users_softwarelicenses WHERE id = $id LIMIT 1";
132 | $DB->query($query);
133 | }
134 | }
135 |
136 | /**
137 | * Count how many users have this license assigned.
138 | *
139 | * @global type $DB
140 | * @param type $softwarelicenses_id
141 | * @return int
142 | */
143 | static function countForLicense($softwarelicenses_id) {
144 | global $DB;
145 |
146 | $query = "SELECT COUNT(`glpi_users_softwarelicenses`.`id`)
147 | FROM `glpi_users_softwarelicenses`
148 | INNER JOIN `glpi_users`
149 | ON (`glpi_users_softwarelicenses`.`users_id` = `glpi_users`.`id`)
150 | WHERE `glpi_users_softwarelicenses`.`softwarelicenses_id` = '$softwarelicenses_id'";
151 |
152 | $result = $DB->query($query);
153 |
154 | if ($DB->numrows($result) != 0) {
155 | return $DB->result($result, 0, 0);
156 | }
157 | return 0;
158 | }
159 |
160 | /**
161 | * Returns the list of users not assigned to a license.
162 | *
163 | * @param int $license_id
164 | *
165 | * @return array
166 | */
167 | private static function usersUnassignedToALicense(int $license_id): array
168 | {
169 | global $DB;
170 |
171 | $result = $DB->request(
172 | 'glpi_users_softwarelicenses',
173 | ['softwarelicenses_id' => $license_id]
174 | );
175 |
176 | $users_assigned_to_a_license = [];
177 |
178 | foreach ($result as $data => $content) {
179 | $users_assigned_to_a_license[] = $content['users_id'];
180 | }
181 |
182 | if (empty($users_assigned_to_a_license)) {
183 | $result = $DB->request('glpi_users');
184 | } else {
185 | $result = $DB->request(
186 | 'glpi_users',
187 | ['NOT' => ['id' => $users_assigned_to_a_license]]
188 | );
189 | }
190 |
191 | $users_unassigned_to_a_license = [];
192 |
193 | foreach ($result as $data => $content) {
194 | $users_unassigned_to_a_license[] = $content['id'];
195 | }
196 |
197 | return $users_unassigned_to_a_license;
198 | }
199 |
200 | /**
201 | * Returns form with users assigned to license.
202 | *
203 | * @param SoftwareLicense $license
204 | * @param int $number_of_assigned_licenses
205 | * @param bool $can_edit
206 | *
207 | * @throws GlpitestSQLError
208 | */
209 | private static function usersAssignedToLicenseForm(
210 | SoftwareLicense $license,
211 | int $number_of_assigned_licenses,
212 | bool $can_edit
213 | ): void {
214 | global $DB;
215 | global $CFG_GLPI;
216 |
217 | $start = isset($_GET['start']) ? $_GET['start'] : 0;
218 | $order = isset($_GET['order']) && ($_GET['order'] === 'DESC') ? $_GET['order'] : 'ASC';
219 | $sort = !empty($_GET['sort']) ? $_GET['sort'] : 'username';
220 |
221 | $license_id = $license->getField('id');
222 | $query = "SELECT `glpi_users_softwarelicenses`.*,
223 | `glpi_users`.`name` AS username,
224 | `glpi_users`.`id` AS userid,
225 | `glpi_users`.`realname` AS userrealname,
226 | `glpi_users`.`firstname` AS userfirstname,
227 | `glpi_softwarelicenses`.`name` AS license,
228 | `glpi_softwarelicenses`.`id` AS lID
229 | FROM `glpi_users_softwarelicenses`
230 | INNER JOIN `glpi_softwarelicenses`
231 | ON (`glpi_users_softwarelicenses`.`softwarelicenses_id`
232 | = `glpi_softwarelicenses`.`id`)
233 | INNER JOIN `glpi_users`
234 | ON (`glpi_users_softwarelicenses`.`users_id` = `glpi_users`.`id`)
235 | WHERE `glpi_softwarelicenses`.`id` = '$license_id'
236 | ORDER BY $sort $order
237 | LIMIT ".intval($start)."," . intval($_SESSION['glpilist_limit']);
238 |
239 | $rand = mt_rand();
240 | if ($result = $DB->query($query)) {
241 | if ($data = $DB->fetchAssoc($result)) {
242 | $parameters = "sort=$sort&order=$order";
243 | $license_page_url = $CFG_GLPI['url_base'] . '/front/softwarelicense.form.php' . '?id=' .
244 | $license_id;
245 | Html::printPager($start, $number_of_assigned_licenses, $license_page_url, $parameters);
246 |
247 | if ($can_edit) {
248 | Html::openMassiveActionsForm('mass' . __CLASS__ . $rand);
249 | list($higher_version, $massive_action_params) =
250 | PluginFpsoftwareVersionhelper::massiveActionParams($rand, __CLASS__);
251 |
252 | $massive_action_params['extraparams']['options']['move']['used'] = [$license_id];
253 | $massive_action_params['extraparams']['options']['move']['softwares_id']
254 | = $license->fields['softwares_id'];
255 | Html::showMassiveActions($higher_version ? $massive_action_params : __CLASS__);
256 | }
257 |
258 | $software = new Software();
259 | $software->getFromDB($license->fields['softwares_id']);
260 |
261 | $text = sprintf(__('%1$s = %2$s'), Software::getTypeName(1), $software->fields["name"]);
262 | $text = sprintf(__('%1$s - ID %2$s'), $text, $license->fields['softwares_id']);
263 | Session::initNavigateListItems('User', $text);
264 |
265 | echo "
";
266 | $columns = [
267 | 'username' => __('Username'),
268 | 'userrealname' => __('Surname'),
269 | 'userfirstname' => __('First name'),
270 | 'added' => __('Added')
271 | ];
272 |
273 | $header_begin = "";
274 | $header_top = '';
275 | $header_bottom = '';
276 | $header_end = '';
277 | if ($can_edit) {
278 | $header_begin .= "";
279 | $header_top .= Html::getCheckAllAsCheckbox('mass' . __CLASS__ . $rand);
280 | $header_bottom .= Html::getCheckAllAsCheckbox('mass' . __CLASS__ . $rand);
281 | $header_end .= " | ";
282 | }
283 |
284 | foreach ($columns as $key => $value) {
285 | $header_end .= "" . $value . " | ";
293 | }
294 |
295 | $header_end .= "
\n";
296 | echo $header_begin . $header_top . $header_end;
297 |
298 | do {
299 | Session::addToNavigateListItems('User', $data["userid"]);
300 | echo "";
301 | if ($can_edit) {
302 | echo "" . Html::getMassiveActionCheckBox(__CLASS__, $data["id"]) . " | ";
303 | }
304 |
305 | $can_show_user = User::canView();
306 | if ($can_show_user) {
307 | echo "" . $data['username'] . " | ";
308 | } else {
309 | echo "" . $data['username'] . " | ";
310 | }
311 |
312 | echo "" . $data['userrealname'] . " | ";
313 | echo "" . $data['userfirstname'] . " | ";
314 | echo "" . $data['added'] . " | ";
315 | echo "
\n";
316 | } while ($data = $DB->fetchAssoc($result));
317 |
318 | echo $header_begin . $header_bottom . $header_end;
319 | echo "
\n";
320 |
321 | if ($can_edit) {
322 | $massive_action_params['ontop'] = false;
323 | Html::showMassiveActions($massive_action_params);
324 | Html::closeForm();
325 | }
326 | } else {
327 | _e('No item found');
328 | }
329 | }
330 |
331 | echo "\n";
332 | }
333 |
334 | /**
335 | * Returns number of users assigned to license.
336 | *
337 | * @param int $license_id
338 | *
339 | * @return int
340 | * @throws GlpitestSQLError
341 | */
342 | private static function numberOfUsersAssignedToLicense(int $license_id): int
343 | {
344 | global $DB;
345 |
346 | $query_number = "SELECT COUNT(*) AS cpt
347 | FROM `glpi_users_softwarelicenses`
348 | INNER JOIN `glpi_users`
349 | ON (`glpi_users_softwarelicenses`.`users_id`
350 | = `glpi_users`.`id`)
351 | WHERE `glpi_users_softwarelicenses`.`softwarelicenses_id` = '$license_id'";
352 |
353 | $number = 0;
354 | if ($result = $DB->query($query_number)) {
355 | $number = $DB->result($result, 0, 0);
356 | }
357 |
358 | return $number;
359 | }
360 |
361 | /**
362 | * Displays form with available users.
363 | *
364 | * @param int $license_id
365 | */
366 | private static function addUserForm(int $license_id): void
367 | {
368 | global $CFG_GLPI;
369 |
370 | $users = self::usersUnassignedToALicense($license_id);
371 |
372 | echo "