File: configlet.xml

package info (click to toggle)
configlet 1.8
  • links: PTS
  • area: main
  • in suites: woody
  • size: 1,044 kB
  • ctags: 211
  • sloc: python: 1,417; xml: 709; makefile: 139; sh: 44
file content (405 lines) | stat: -rw-r--r-- 16,088 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  <chapter id="ch-configlet">
    <chapterinfo>
      <releaseinfo>$Progeny: configlet.xml,v 1.11 2002/01/18 06:19:44 dsp Exp $</releaseinfo>
    </chapterinfo>

    <title>Writing a configlet</title>

    <section>
      <title>Files</title>

      <para>Configlets consist of several files, all contained in a
      common directory.  The minimal files included in this directory
      are:</para>

      <variablelist>
        <varlistentry>
          <term><filename>main.glade</filename></term>
          <listitem>
            <para>the Glade XML file that describes the user interface
            for the configlet.  Creating and editing Glade files is
            beyond the scope of this document.  Refer to <ulink
            url="http://glade.gnome.org/">the Glade web page</ulink>
            for more information.</para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term><filename>main.py</filename></term>
          <listitem>
            <para>the Python code that implements the "meat" of the
            configlet's code.</para>
          </listitem>
        </varlistentry>
      </variablelist>

      <para>These are the only two files directly referenced by the
      infrastructure classes.  The developer is free to include new
      files or subdirectories in addition to these two.  For example,
      some configlets may include icons or other graphics.</para>

      <para>The Glade file has two requirements:</para>

      <itemizedlist>
        <listitem>
          <para>It must contain widgets that correspond to the
          configlet pages that the configlet needs to display.  Each
          of these widgets must have a name that will be used by the
          configlet <ulink url="api/index.html">API</ulink>.  Since
          these widgets will be inserted into a parent object created
          by the front ends, they should not be toplevel objects; they
          may be wrapped by a toplevel to allow them to be edited in
          Glade, as long as those toplevel objects can be discarded at
          runtime.  As a special case, single-page configlets may name
          their page widget <literal>mainwidget</literal>; this will
          enable some automatic handling by the <ulink
          url="api/index.html">API</ulink>.</para>
        </listitem>

        <listitem>
          <para>The configlet page widgets must not be set visible by
          default; they are set visible as a result of the configlet
          module when appropriate.</para>
        </listitem>
      </itemizedlist>

    </section>

    <section>
      <title>Python code</title>

      <para>The Python file contains the code for the configlet.  It
      must follow these rules:</para>

      <itemizedlist>
        <listitem>
          <para>It must <literal>import configlet</literal>.</para>
        </listitem>

        <listitem>
          <para>It must define a class derived from <ulink
          url="api/configlet_Configlet.py.html"><classname>configlet.Configlet</classname></ulink>.
          It must provide a <ulink
          url="api/configlet_Configlet.py.html#gnome_setup"><function>gnome_setup</function></ulink>
          method which does any GUI-specific initialization.  For example,
          Etherconf's configlet uses <ulink
          url="api/configlet_Configlet.py.html#gnome_setup"><function>gnome_setup</function></ulink>
          to retrieve the widgets it uses from the wtree, get a list of
          network interfaces, and the following bit of code for
          automatically connecting signal handlers.</para>

          <example>
            <title>Etherconf gnome_setup</title>
            <programlisting>
<![CDATA[
        dict = {}
        global Etherconf
        for key in dir(Etherconf):
            dict[key] = getattr(self, key)
        self.wtree.signal_autoconnect(dict)
]]>
            </programlisting>

          </example>
        </listitem>

        <listitem>
          <para>It must define a dictionary of attributes, which are
          used to initialize the configlet.  At least the following
          attributes must be set:</para>

          <itemizedlist>
            <listitem>
              <para><literal>display_title</literal>: This should be a
              short descriptive name for the configlet.  Examples of
              good names would include "X Configuration" or "Network";
              some bad ones would include "Configure the X Server's
              Display Settings" (too long) or "etherconf" (too short
              and user-hostile).</para>
            </listitem>
            <listitem>
              <para><literal>description</literal>: This should be a
              longer description of the configlet.</para>
            </listitem>
            <listitem>
              <para><literal>packages</literal>: This must be a list
              of the packages the configlet configures.</para>
            </listitem>
          </itemizedlist>

          <para>Additionally, the <literal>page_names</literal>
          attribute must be defined as a list of the names of the
          configlet page widgets.  Only one exception is allowed:
          single-page configlets may omit this attribute if the name
          for its page widget is <literal>mainwidget</literal>.</para>

          <para>Multi-page configlets must also define a
          <literal>page_display_titles</literal> attribute.  This
          should be a dictionary containing short descriptive titles
          for each page of the configlet.</para>

          <para>Optionally, the <literal>priority</literal> attribute
          may be defined.  This should be a number between 1 and 100;
          1 is considered to be the highest priority, and will be
          displayed first, most prominently, or however the front end
          chooses to interpret priority.  This value is entirely
          advisory, and may not make sense in all front ends.  If not
          set, the <ulink url="api/index.html">API</ulink> will set it
          to the default value of 50.</para>

          <para>This list of attributes is not exhaustive; the
          configlet may decide to set other attributes for other uses.
          See the <ulink url="api/index.html">API Reference</ulink>
          for details.</para>

          <para>This dictionary of attributes must be passed to <ulink
          url="api/configlet.py.html#register_configlet"><function>configlet.register_configlet(<replaceable>classname</replaceable>,
          <replaceable>attributes</replaceable>)</function></ulink>.</para>

          <example>
            <title>Etherconf attributes and registration</title>
            <programlisting>
<![CDATA[
_attrs = { "name": "etherconf",
           "display_title": _("Configure Network Interfaces"),
           "description": _("Select this option to configure the devices your system uses to access networks such as the Internet."),
           "packages": ["etherconf", "postfix"]
}

configlet.register_configlet(Etherconf, _attrs)
]]>
            </programlisting>
          </example>
        </listitem>

      </itemizedlist>

      <para>If your configlet is a front end to Debconf questions (most
      are), you will also want to define <ulink
      url="api/configlet_Configlet.py.html#load_debconf"><function>load_debconf</function></ulink>
      and <ulink
      url="api/configlet_Configlet.py.html#report_debconf"><function>report_debconf</function></ulink>
      methods.  <ulink
      url="api/configlet_Configlet.py.html#load_debconf"><function>load_debconf</function></ulink>
      receives as the list of all Debconf values, not just those pertaining
      to the packages this configlet configures.  It is up to the configlet
      to determine which values it is interested in.</para>

      <example>
        <title>Etherconf load_debconf</title>
        <programlisting>
<![CDATA[
    def load_debconf(self, dcdata):
        self.mail_config = []

        for i in dcdata:
            (template, question, value) = re.split(r"\s+", i, 2)
            (package, varname) = re.split("/", question, 1)

            if package == "postfix":
                self.mail_config.append(i)

            if package != "etherconf":
                continue

            if value == '""' or value == "none":
                value = ""

            if varname == "INT-devices":
                # We don't need to read this one, but we do need to
                # set it.  See debconf() below.
                pass
            elif varname == "hostname":
                if value:
                    self.hostname_entry.set_text(value)
            elif varname == "domainname":
                if value:
                    self.domainname_entry.set_text(value)
            elif varname == "nameservers":
                if value:
                    self.nameservers_entry.set_text(value)

            else:
                # If a ValueError occurs, this is not a dhcp-p:eth0
                # type question (which is what we're after), so it
                # doesn't matter what it is, we're simply not
                # interested.
                try:
                    (varname, device) = re.split(":", varname, 1)
                except ValueError:
                    continue

                info = self.get_device_info(device)
                if varname == "configure":
                    if value == "true":
                        info.configure = TRUE
                    else:
                        info.configure = FALSE
                elif varname == "removable":
                    if value == "true":
                        info.removable = TRUE
                    else:
                        info.removable = FALSE
                elif varname == "dhcp-p":
                    if value == "true":
                        info.dhcp = TRUE
                    else:
                        info.dhcp = FALSE
                elif varname == "dhcphost":
                    if value:
                        info.dhcphost = value
                elif varname == "ipaddr":
                    if value:
                        info.ip = value
                elif varname == "netmask":
                    if value:
                        info.netmask = value
                elif varname == "gateway":
                    if value:
                        info.gateway = value

        self.setup_menu(self.devices)
        self.change_device(self.devices[0])
]]>
        </programlisting>
      </example>

      <example>
        <title>Etherconf report_debconf</title>
        <programlisting>
<![CDATA[
    def _dcstring(var, val, device=None):
        if device:
            s = "etherconf/%s etherconf/%s:%s %s" % (var, var, device, val)
        else:
            s = "etherconf/%s etherconf/%s %s" % (var, var, val)

        debug("Reporting %s" % (s,))
        return s

    def report_debconf(self):
        results = []
        int_devices = ""

        results.extend(self.mail_config)
        results.append("postfix/relayhost postfix/relayhost %s"
                       % (self.relayhost,))
        results.append("postfix/mailname postfix/mailname %s.%s"
                       % (self.hostname, self.domainname))

        results.append(_dcstring("replace-existing-files", "true"))
        results.append(_dcstring("hostname", self.hostname))
        results.append(_dcstring("domainname", self.domainname))
        results.append(_dcstring("nameservers", self.nameservers))

        for i in self.devices:
            int_devices = "%s:%s" % (int_devices, i)
            info = self.get_device_info(i)

            if info.configure:
                results.append(_dcstring("configure", "true", i))
            else:
                results.append(_dcstring("configure", "false", i))

            if info.removable:
                results.append(_dcstring("removable", "true", i))
            else:
                results.append(_dcstring("removable", "false", i))

            if info.dhcp:
                results.append(_dcstring("dhcp-p", "true", i))
            else:
                results.append(_dcstring("dhcp-p", "false", i))

            results.append(_dcstring("dhcphost", info.dhcphost, i))
            results.append(_dcstring("ipaddr", info.ip, i))
            results.append(_dcstring("netmask", info.netmask, i))
            results.append(_dcstring("gateway", info.gateway, i))

        results.append(_dcstring("INT-devices", int_devices[1:]))

        return results
]]>
        </programlisting>
      </example>

      <para>Configlet directories must be located in a standard place,
      <filename>/usr/share/configlets/</filename>, where front ends
      can find them easily.  Note that the directory name under
      <filename>/usr/share/configlets</filename> is arbitrary, but
      should be descriptive and unlikely to risk a collision with
      other configlets, and must not contain spaces or other unusual
      characters.</para>

      <para>Here is a sample hierarchy.</para>

      <literallayout class="monospaced">
 /usr/share/configlets
   |
   +--etherconf
   |   |
   |   +--main.glade
   |   +--main.py
   |
   +--timezoneconf
   |   |
   |   +--main.glade
   |   +--main.py
   |
 (and so on)
      </literallayout>

      <para>Any package providing a configlet must call
      <command>update-configlets <option>--install</option>
      <parameter>directory_name</parameter></command> in its
      <filename>postinst</filename> and <command>update-configlets
      <option>--remove</option>
      <parameter>directory_name</parameter></command> in its
      <filename>prerm</filename>; this allows the configlet system to
      register configlets with front ends that require registration of
      some kind.  This will require that the package either test for
      <filename>/usr/sbin/update-configlets</filename> in the
      <filename>postinst</filename> or declare a dependency on the
      configlet package.</para>
    </section>

    <section id="multi-page-configlets">
      <title>Multi-Page Configlets</title>

      <para>For a standard configlet, this is all you need.  For very
      complex configlets, however, you might want several pages of
      configuration screens; this shouldn't be done with a simple
      tabbed widget to prevent nested tab widgets (if the front end
      implements tabs, for example).  Instead, implement a multi-page
      configlet.  The front end can then present the pages in whichever
      way makes sense.</para>

      <para>To implement a multipage configlet, create your multiple
      pages in Glade and save them in <filename>main.glade</filename>.
      For each page, the container widget that envelops the entire
      page should be named with a sensible name.  Then, set the
      attribute <literal>page_names</literal> to a list of these
      names.  Optionally, you may set the
      <literal>page_display_titles</literal> attribute as well; this
      should be a mapping between page names (as in the
      <literal>page_names</literal> attribute) and the short
      descriptive titles for each page.</para>

      <para>You can also define <function>validate_page</function>
      that can be called by the front end to validate a particular
      page's input.  As with <function>validate</function>, this
      function is advisory only; there are no guarantees that it will
      be called at all, that it will be called in any particular
      order, or that its results will not be ignored.  It returns a
      similar value to <function>validate</function>, and takes the
      name of the page to validate as its only argument.  The default
      definitions for <function>validate</function> and
      <function>validate_page</function> complement each other; thus,
      a configlet should only override one or the other.</para>

    </section>

  </chapter>

<!--  Local variables: -->
<!--  eval: (sgml-load-dtd "../../doctools/docbook.ced") -->
<!--  End: -->